ContentViewCore.java revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
1e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch// Copyright 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)package org.chromium.content.browser;
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.annotation.SuppressLint;
89ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdochimport android.app.Activity;
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.app.SearchManager;
10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.content.ContentResolver;
1190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import android.content.Context;
12a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import android.content.Intent;
1390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import android.content.pm.PackageManager;
14e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport android.content.res.Configuration;
1590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import android.database.ContentObserver;
16868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.graphics.Bitmap;
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.graphics.Canvas;
18868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.graphics.Color;
19868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.graphics.Rect;
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.net.Uri;
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.os.Build;
22868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.os.Bundle;
23868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.os.Handler;
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.os.ResultReceiver;
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.os.SystemClock;
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.provider.Browser;
27868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.provider.Settings;
28868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.text.Editable;
29868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.text.Selection;
30868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.text.TextUtils;
31868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.util.Log;
32868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.view.ActionMode;
33868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.view.HapticFeedbackConstants;
34f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)import android.view.InputDevice;
35868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.view.KeyEvent;
36868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.view.MotionEvent;
37868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.view.View;
38868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.view.ViewGroup;
39868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.view.accessibility.AccessibilityEvent;
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.view.accessibility.AccessibilityManager;
41d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
42868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.view.accessibility.AccessibilityNodeInfo;
43868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.view.accessibility.AccessibilityNodeProvider;
44868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.view.inputmethod.EditorInfo;
45868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.view.inputmethod.InputConnection;
46868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.view.inputmethod.InputMethodManager;
47868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import android.widget.FrameLayout;
4890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import com.google.common.annotations.VisibleForTesting;
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.chromium.base.CalledByNative;
52e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport org.chromium.base.CommandLine;
53e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport org.chromium.base.JNINamespace;
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.chromium.base.ObserverList;
55868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import org.chromium.base.ObserverList.RewindableIterator;
56868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import org.chromium.base.TraceEvent;
5790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import org.chromium.content.R;
5890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import org.chromium.content.browser.ScreenOrientationListener.ScreenOrientationObserver;
5990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import org.chromium.content.browser.accessibility.AccessibilityInjector;
6090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import org.chromium.content.browser.accessibility.BrowserAccessibilityManager;
6190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import org.chromium.content.browser.input.AdapterInputConnection;
6290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import org.chromium.content.browser.input.HandleView;
63e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport org.chromium.content.browser.input.ImeAdapter;
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.chromium.content.browser.input.ImeAdapter.AdapterInputConnectionFactory;
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.chromium.content.browser.input.InputMethodManagerWrapper;
66868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import org.chromium.content.browser.input.InsertionHandleController;
67868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import org.chromium.content.browser.input.SelectPopupDialog;
68868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import org.chromium.content.browser.input.SelectPopupItem;
69868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import org.chromium.content.browser.input.SelectionHandleController;
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import org.chromium.content.common.ContentSwitches;
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.chromium.content_public.browser.GestureStateListener;
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.chromium.content_public.browser.WebContents;
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.chromium.ui.base.ViewAndroid;
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.chromium.ui.base.ViewAndroidDelegate;
75868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import org.chromium.ui.base.WindowAndroid;
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.chromium.ui.gfx.DeviceDisplayInfo;
77868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
78868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import java.lang.annotation.Annotation;
79868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import java.lang.reflect.Field;
801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport java.util.ArrayList;
811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport java.util.HashMap;
821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport java.util.HashSet;
831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport java.util.List;
84868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)import java.util.Map;
85868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
86868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)/**
87868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) * Provides a Java-side 'wrapper' around a WebContent (native) instance.
88868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) * Contains all the major functionality necessary to manage the lifecycle of a ContentView without
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * being tied to the view system.
90868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) */
91868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)@JNINamespace("content")
92868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)public class ContentViewCore
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        implements NavigationClient, AccessibilityStateChangeListener, ScreenOrientationObserver {
94868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
95868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    private static final String TAG = "ContentViewCore";
96868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Used to avoid enabling zooming in / out if resulting zooming will
98868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    // produce little visible difference.
99868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    private static final float ZOOM_CONTROLS_EPSILON = 0.007f;
100868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
101868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    // Used to represent gestures for long press and long tap.
102868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    private static final int IS_LONG_PRESS = 1;
103effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    private static final int IS_LONG_TAP = 2;
104effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
105effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    // Length of the delay (in ms) before fading in handles after the last page movement.
106effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    private static final int TEXT_HANDLE_FADE_IN_DELAY = 300;
107effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
108effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    // If the embedder adds a JavaScript interface object that contains an indirect reference to
109effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    // the ContentViewCore, then storing a strong ref to the interface object on the native
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // side would prevent garbage collection of the ContentViewCore (as that strong ref would
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // create a new GC root).
112868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    // For that reason, we store only a weak reference to the interface object on the
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // native side. However we still need a strong reference on the Java side to
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // prevent garbage collection if the embedder doesn't maintain their own ref to the
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // interface object - the Java side ref won't create a new GC root.
116868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    // This map stores those refernces. We put into the map on addJavaScriptInterface()
117868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    // and remove from it in removeJavaScriptInterface().
118868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    private final Map<String, Object> mJavaScriptInterfaces = new HashMap<String, Object>();
119868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Additionally, we keep track of all Java bound JS objects that are in use on the
12190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    // current page to ensure that they are not garbage collected until the page is
122868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    // navigated. This includes interface objects that have been removed
12390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    // via the removeJavaScriptInterface API and transient objects returned from methods
12490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    // on the interface object. Note we use HashSet rather than Set as the native side
125868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    // expects HashSet (no bindings for interfaces).
12690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    private final HashSet<Object> mRetainedJavaScriptObjects = new HashSet<Object>();
127868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
128868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    /**
129868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)     * Interface that consumers of {@link ContentViewCore} must implement to allow the proper
130868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)     * dispatching of view methods through the containing view.
131868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)     *
13290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)     * <p>
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * All methods with the "super_" prefix should be routed to the parent of the
134868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)     * implementing container view.
135868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)     */
136868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    @SuppressWarnings("javadoc")
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public interface InternalAccessDelegate {
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        /**
139868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         * @see View#drawChild(Canvas, View, long)
140e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch         */
141e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        boolean drawChild(Canvas canvas, View child, long drawingTime);
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        /**
144868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         * @see View#onKeyUp(keyCode, KeyEvent)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         */
146868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        boolean super_onKeyUp(int keyCode, KeyEvent event);
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        /**
149868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         * @see View#dispatchKeyEventPreIme(KeyEvent)
150868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         */
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        boolean super_dispatchKeyEventPreIme(KeyEvent event);
152868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        /**
154868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         * @see View#dispatchKeyEvent(KeyEvent)
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         */
156868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        boolean super_dispatchKeyEvent(KeyEvent event);
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        /**
15990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)         * @see View#onGenericMotionEvent(MotionEvent)
16090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)         */
16190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        boolean super_onGenericMotionEvent(MotionEvent event);
16290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
16390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        /**
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         * @see View#onConfigurationChanged(Configuration)
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         */
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        void super_onConfigurationChanged(Configuration newConfig);
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        /**
169868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         * @see View#onScrollChanged(int, int, int, int)
170868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         */
171868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix);
172868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
173868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        /**
174868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         * @see View#awakenScrollBars()
175868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         */
176868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        boolean awakenScrollBars();
177868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
178868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        /**
179868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         * @see View#awakenScrollBars(int, boolean)
1805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)         */
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        boolean super_awakenScrollBars(int startDelay, boolean invalidate);
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
184eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    /**
18590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)     * An interface for controlling visibility and state of embedder-provided zoom controls.
18690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)     */
18790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    public interface ZoomControlsDelegate {
18890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        /**
18990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)         * Called when it's reasonable to show zoom controls.
19090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)         */
19190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        void invokeZoomPicker();
19290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        /**
194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)         * Called when zoom controls need to be hidden (e.g. when the view hides).
19590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)         */
196f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        void dismissZoomPicker();
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
198f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        /**
199f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)         * Called when page scale has been changed, so the controls can update their state.
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         */
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        void updateZoomControls();
202868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    }
203e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
204e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    /**
205d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)     * An interface that allows the embedder to be notified when the results of
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     * extractSmartClipData are available.
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     */
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public interface SmartClipDataListener {
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        public void onSmartClipDataExtracted(String result);
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private VSyncManager.Provider mVSyncProvider;
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private VSyncManager.Listener mVSyncListener;
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private int mVSyncSubscriberCount;
215    private boolean mVSyncListenerRegistered;
216
217    // To avoid IPC delay we use input events to directly trigger a vsync signal in the renderer.
218    // When we do this, we also need to avoid sending the real vsync signal for the current
219    // frame to avoid double-ticking. This flag is used to inhibit the next vsync notification.
220    private boolean mDidSignalVSyncUsingInputEvent;
221
222    public VSyncManager.Listener getVSyncListener(VSyncManager.Provider vsyncProvider) {
223        if (mVSyncProvider != null && mVSyncListenerRegistered) {
224            mVSyncProvider.unregisterVSyncListener(mVSyncListener);
225            mVSyncListenerRegistered = false;
226        }
227
228        mVSyncProvider = vsyncProvider;
229        mVSyncListener = new VSyncManager.Listener() {
230            @Override
231            public void updateVSync(long tickTimeMicros, long intervalMicros) {
232                if (mNativeContentViewCore != 0) {
233                    nativeUpdateVSyncParameters(mNativeContentViewCore, tickTimeMicros,
234                            intervalMicros);
235                }
236            }
237
238            @Override
239            public void onVSync(long frameTimeMicros) {
240                animateIfNecessary(frameTimeMicros);
241
242                if (mRequestedVSyncForInput) {
243                    mRequestedVSyncForInput = false;
244                    removeVSyncSubscriber();
245                }
246                if (mNativeContentViewCore != 0) {
247                    nativeOnVSync(mNativeContentViewCore, frameTimeMicros);
248                }
249            }
250        };
251
252        if (mVSyncSubscriberCount > 0) {
253            // addVSyncSubscriber() is called before getVSyncListener.
254            vsyncProvider.registerVSyncListener(mVSyncListener);
255            mVSyncListenerRegistered = true;
256        }
257
258        return mVSyncListener;
259    }
260
261    @CalledByNative
262    void addVSyncSubscriber() {
263        if (!isVSyncNotificationEnabled()) {
264            mDidSignalVSyncUsingInputEvent = false;
265        }
266        if (mVSyncProvider != null && !mVSyncListenerRegistered) {
267            mVSyncProvider.registerVSyncListener(mVSyncListener);
268            mVSyncListenerRegistered = true;
269        }
270        mVSyncSubscriberCount++;
271    }
272
273    @CalledByNative
274    void removeVSyncSubscriber() {
275        if (mVSyncProvider != null && mVSyncSubscriberCount == 1) {
276            assert mVSyncListenerRegistered;
277            mVSyncProvider.unregisterVSyncListener(mVSyncListener);
278            mVSyncListenerRegistered = false;
279        }
280        mVSyncSubscriberCount--;
281        assert mVSyncSubscriberCount >= 0;
282    }
283
284    @CalledByNative
285    private void resetVSyncNotification() {
286        while (isVSyncNotificationEnabled()) removeVSyncSubscriber();
287        mVSyncSubscriberCount = 0;
288        mVSyncListenerRegistered = false;
289        mNeedAnimate = false;
290    }
291
292    private boolean isVSyncNotificationEnabled() {
293        return mVSyncProvider != null && mVSyncListenerRegistered;
294    }
295
296    @CalledByNative
297    private void setNeedsAnimate() {
298        if (!mNeedAnimate) {
299            mNeedAnimate = true;
300            addVSyncSubscriber();
301        }
302    }
303
304    private final Context mContext;
305    private ViewGroup mContainerView;
306    private InternalAccessDelegate mContainerViewInternals;
307    private WebContents mWebContents;
308    private WebContentsObserverAndroid mWebContentsObserver;
309
310    private ContentViewClient mContentViewClient;
311
312    private ContentSettings mContentSettings;
313
314    // Native pointer to C++ ContentViewCoreImpl object which will be set by nativeInit().
315    private long mNativeContentViewCore = 0;
316
317    private boolean mInForeground = false;
318
319    private final ObserverList<GestureStateListener> mGestureStateListeners;
320    private final RewindableIterator<GestureStateListener> mGestureStateListenersIterator;
321    private ZoomControlsDelegate mZoomControlsDelegate;
322
323    private PopupZoomer mPopupZoomer;
324    private SelectPopupDialog mSelectPopupDialog;
325
326    private Runnable mFakeMouseMoveRunnable = null;
327
328    // Only valid when focused on a text / password field.
329    private ImeAdapter mImeAdapter;
330    private ImeAdapter.AdapterInputConnectionFactory mAdapterInputConnectionFactory;
331    private AdapterInputConnection mInputConnection;
332    private InputMethodManagerWrapper mInputMethodManagerWrapper;
333
334    private SelectionHandleController mSelectionHandleController;
335    private InsertionHandleController mInsertionHandleController;
336
337    private Runnable mDeferredHandleFadeInRunnable;
338
339    private PositionObserver mPositionObserver;
340    private PositionObserver.Listener mPositionListener;
341
342    // Size of the viewport in physical pixels as set from onSizeChanged.
343    private int mViewportWidthPix;
344    private int mViewportHeightPix;
345    private int mPhysicalBackingWidthPix;
346    private int mPhysicalBackingHeightPix;
347    private int mOverdrawBottomHeightPix;
348    private int mViewportSizeOffsetWidthPix;
349    private int mViewportSizeOffsetHeightPix;
350    private int mLocationInWindowX;
351    private int mLocationInWindowY;
352
353    // Cached copy of all positions and scales as reported by the renderer.
354    private final RenderCoordinates mRenderCoordinates;
355
356    private final RenderCoordinates.NormalizedPoint mStartHandlePoint;
357    private final RenderCoordinates.NormalizedPoint mEndHandlePoint;
358    private final RenderCoordinates.NormalizedPoint mInsertionHandlePoint;
359
360    // Cached copy of the visible rectangle defined by two points. Needed to determine
361    // visibility of insertion/selection handles.
362    private final RenderCoordinates.NormalizedPoint mTopLeftVisibilityClippingPoint;
363    private final RenderCoordinates.NormalizedPoint mBottomRightVisibilityClippingPoint;
364
365    // Tracks whether a selection is currently active.  When applied to selected text, indicates
366    // whether the last selected text is still highlighted.
367    private boolean mHasSelection;
368    private String mLastSelectedText;
369    private boolean mSelectionEditable;
370    private ActionMode mActionMode;
371    private boolean mUnselectAllOnActionModeDismiss;
372
373    // Delegate that will handle GET downloads, and be notified of completion of POST downloads.
374    private ContentViewDownloadDelegate mDownloadDelegate;
375
376    // The AccessibilityInjector that handles loading Accessibility scripts into the web page.
377    private AccessibilityInjector mAccessibilityInjector;
378
379    // Whether native accessibility, i.e. without any script injection, is allowed.
380    private boolean mNativeAccessibilityAllowed;
381
382    // Whether native accessibility, i.e. without any script injection, has been enabled.
383    private boolean mNativeAccessibilityEnabled;
384
385    // Handles native accessibility, i.e. without any script injection.
386    private BrowserAccessibilityManager mBrowserAccessibilityManager;
387
388    // System accessibility service.
389    private final AccessibilityManager mAccessibilityManager;
390
391    // Accessibility touch exploration state.
392    private boolean mTouchExplorationEnabled;
393
394    // Allows us to dynamically respond when the accessibility script injection flag changes.
395    private ContentObserver mAccessibilityScriptInjectionObserver;
396
397    // Temporary notification to tell onSizeChanged to focus a form element,
398    // because the OSK was just brought up.
399    private final Rect mFocusPreOSKViewportRect = new Rect();
400
401    // Whether we received a new frame since consumePendingRendererFrame() was last called.
402    private boolean mPendingRendererFrame = false;
403
404    // Whether we should animate at the next vsync tick.
405    private boolean mNeedAnimate = false;
406
407    // Whether we requested a proactive vsync event in response to touch input.
408    // This reduces the latency of responding to input by ensuring the renderer
409    // is sent a BeginFrame for every touch event we receive. Otherwise the
410    // renderer's SetNeedsBeginFrame message would get serviced at the next
411    // vsync.
412    private boolean mRequestedVSyncForInput = false;
413
414    // On single tap this will store the x, y coordinates of the touch.
415    private int mSingleTapX;
416    private int mSingleTapY;
417
418    // Whether a touch scroll sequence is active, used to hide text selection
419    // handles. Note that a scroll sequence will *always* bound a pinch
420    // sequence, so this will also be true for the duration of a pinch gesture.
421    private boolean mTouchScrollInProgress;
422
423    // The outstanding fling start events that hasn't got fling end yet. It may be > 1 because
424    // onNativeFlingStopped() is called asynchronously.
425    private int mPotentiallyActiveFlingCount;
426
427    private ViewAndroid mViewAndroid;
428
429    private SmartClipDataListener mSmartClipDataListener = null;
430
431    // This holds the state of editable text (e.g. contents of <input>, contenteditable) of
432    // a focused element.
433    // Every time the user, IME, javascript (Blink), autofill etc. modifies the content, the new
434    //  state must be reflected to this to keep consistency.
435    private Editable mEditable;
436
437    /**
438     * PID used to indicate an invalid render process.
439     */
440    // Keep in sync with the value returned from ContentViewCoreImpl::GetCurrentRendererProcessId()
441    // if there is no render process.
442    public static final int INVALID_RENDER_PROCESS_PID = 0;
443
444    /**
445     * Constructs a new ContentViewCore. Embedders must call initialize() after constructing
446     * a ContentViewCore and before using it.
447     *
448     * @param context The context used to create this.
449     */
450    public ContentViewCore(Context context) {
451        mContext = context;
452
453        mAdapterInputConnectionFactory = new AdapterInputConnectionFactory();
454        mInputMethodManagerWrapper = new InputMethodManagerWrapper(mContext);
455
456        mRenderCoordinates = new RenderCoordinates();
457        float deviceScaleFactor = getContext().getResources().getDisplayMetrics().density;
458        String forceScaleFactor = CommandLine.getInstance().getSwitchValue(
459                ContentSwitches.FORCE_DEVICE_SCALE_FACTOR);
460        if (forceScaleFactor != null) {
461            deviceScaleFactor = Float.valueOf(forceScaleFactor);
462        }
463        mRenderCoordinates.setDeviceScaleFactor(deviceScaleFactor);
464        mStartHandlePoint = mRenderCoordinates.createNormalizedPoint();
465        mEndHandlePoint = mRenderCoordinates.createNormalizedPoint();
466        mInsertionHandlePoint = mRenderCoordinates.createNormalizedPoint();
467        mTopLeftVisibilityClippingPoint = mRenderCoordinates.createNormalizedPoint();
468        mBottomRightVisibilityClippingPoint = mRenderCoordinates.createNormalizedPoint();
469        mAccessibilityManager = (AccessibilityManager)
470                getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
471        mGestureStateListeners = new ObserverList<GestureStateListener>();
472        mGestureStateListenersIterator = mGestureStateListeners.rewindableIterator();
473
474        mEditable = Editable.Factory.getInstance().newEditable("");
475        Selection.setSelection(mEditable, 0);
476    }
477
478    /**
479     * @return The context used for creating this ContentViewCore.
480     */
481    @CalledByNative
482    public Context getContext() {
483        return mContext;
484    }
485
486    /**
487     * @return The ViewGroup that all view actions of this ContentViewCore should interact with.
488     */
489    public ViewGroup getContainerView() {
490        return mContainerView;
491    }
492
493    /**
494     * @return The WebContents currently being rendered.
495     */
496    public WebContents getWebContents() {
497        return mWebContents;
498    }
499
500    /**
501     * Specifies how much smaller the WebKit layout size should be relative to the size of this
502     * view.
503     * @param offsetXPix The X amount in pixels to shrink the viewport by.
504     * @param offsetYPix The Y amount in pixels to shrink the viewport by.
505     */
506    public void setViewportSizeOffset(int offsetXPix, int offsetYPix) {
507        if (offsetXPix != mViewportSizeOffsetWidthPix ||
508                offsetYPix != mViewportSizeOffsetHeightPix) {
509            mViewportSizeOffsetWidthPix = offsetXPix;
510            mViewportSizeOffsetHeightPix = offsetYPix;
511            if (mNativeContentViewCore != 0) nativeWasResized(mNativeContentViewCore);
512        }
513    }
514
515    /**
516     * Returns a delegate that can be used to add and remove views from the ContainerView.
517     *
518     * NOTE: Use with care, as not all ContentViewCore users setup their ContainerView in the same
519     * way. In particular, the Android WebView has limitations on what implementation details can
520     * be provided via a child view, as they are visible in the API and could introduce
521     * compatibility breaks with existing applications. If in doubt, contact the
522     * android_webview/OWNERS
523     *
524     * @return A ViewAndroidDelegate that can be used to add and remove views.
525     */
526    @VisibleForTesting
527    public ViewAndroidDelegate getViewAndroidDelegate() {
528        return new ViewAndroidDelegate() {
529            @Override
530            public View acquireAnchorView() {
531                View anchorView = new View(getContext());
532                mContainerView.addView(anchorView);
533                return anchorView;
534            }
535
536            @Override
537            @SuppressWarnings("deprecation")  // AbsoluteLayout
538            public void setAnchorViewPosition(
539                    View view, float x, float y, float width, float height) {
540                assert view.getParent() == mContainerView;
541
542                float scale = (float) DeviceDisplayInfo.create(getContext()).getDIPScale();
543
544                // The anchor view should not go outside the bounds of the ContainerView.
545                int leftMargin = Math.round(x * scale);
546                int topMargin = Math.round(mRenderCoordinates.getContentOffsetYPix() + y * scale);
547                int scaledWidth = Math.round(width * scale);
548                // ContentViewCore currently only supports these two container view types.
549                if (mContainerView instanceof FrameLayout) {
550                    if (scaledWidth + leftMargin > mContainerView.getWidth()) {
551                        scaledWidth = mContainerView.getWidth() - leftMargin;
552                    }
553                    FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
554                        scaledWidth, Math.round(height * scale));
555                    lp.leftMargin = leftMargin;
556                    lp.topMargin = topMargin;
557                    view.setLayoutParams(lp);
558                } else if (mContainerView instanceof android.widget.AbsoluteLayout) {
559                    // This fixes the offset due to a difference in
560                    // scrolling model of WebView vs. Chrome.
561                    // TODO(sgurun) fix this to use mContainerView.getScroll[X/Y]()
562                    // as it naturally accounts for scroll differences between
563                    // these models.
564                    leftMargin += mRenderCoordinates.getScrollXPixInt();
565                    topMargin += mRenderCoordinates.getScrollYPixInt();
566
567                    android.widget.AbsoluteLayout.LayoutParams lp =
568                            new android.widget.AbsoluteLayout.LayoutParams(
569                                scaledWidth, (int) (height * scale), leftMargin, topMargin);
570                    view.setLayoutParams(lp);
571                } else {
572                    Log.e(TAG, "Unknown layout " + mContainerView.getClass().getName());
573                }
574            }
575
576            @Override
577            public void releaseAnchorView(View anchorView) {
578                mContainerView.removeView(anchorView);
579            }
580        };
581    }
582
583    @VisibleForTesting
584    public void setImeAdapterForTest(ImeAdapter imeAdapter) {
585        mImeAdapter = imeAdapter;
586    }
587
588    @VisibleForTesting
589    public ImeAdapter getImeAdapterForTest() {
590        return mImeAdapter;
591    }
592
593    @VisibleForTesting
594    public void setAdapterInputConnectionFactory(AdapterInputConnectionFactory factory) {
595        mAdapterInputConnectionFactory = factory;
596    }
597
598    @VisibleForTesting
599    public void setInputMethodManagerWrapperForTest(InputMethodManagerWrapper immw) {
600        mInputMethodManagerWrapper = immw;
601    }
602
603    @VisibleForTesting
604    public AdapterInputConnection getInputConnectionForTest() {
605        return mInputConnection;
606    }
607
608    @VisibleForTesting
609    public void setContainerViewForTest(ViewGroup view) {
610        mContainerView = view;
611    }
612
613    private ImeAdapter createImeAdapter(Context context) {
614        return new ImeAdapter(mInputMethodManagerWrapper,
615                new ImeAdapter.ImeAdapterDelegate() {
616                    @Override
617                    public void onImeEvent(boolean isFinish) {
618                        getContentViewClient().onImeEvent();
619                        if (!isFinish) {
620                            hideHandles();
621                        }
622                    }
623
624                    @Override
625                    public void onSetFieldValue() {
626                        scrollFocusedEditableNodeIntoView();
627                    }
628
629                    @Override
630                    public void onDismissInput() {
631                        getContentViewClient().onImeStateChangeRequested(false);
632                    }
633
634                    @Override
635                    public View getAttachedView() {
636                        return mContainerView;
637                    }
638
639                    @Override
640                    public ResultReceiver getNewShowKeyboardReceiver() {
641                        return new ResultReceiver(new Handler()) {
642                            @Override
643                            public void onReceiveResult(int resultCode, Bundle resultData) {
644                                getContentViewClient().onImeStateChangeRequested(
645                                        resultCode == InputMethodManager.RESULT_SHOWN ||
646                                        resultCode == InputMethodManager.RESULT_UNCHANGED_SHOWN);
647                                if (resultCode == InputMethodManager.RESULT_SHOWN) {
648                                    // If OSK is newly shown, delay the form focus until
649                                    // the onSizeChanged (in order to adjust relative to the
650                                    // new size).
651                                    // TODO(jdduke): We should not assume that onSizeChanged will
652                                    // always be called, crbug.com/294908.
653                                    getContainerView().getWindowVisibleDisplayFrame(
654                                            mFocusPreOSKViewportRect);
655                                } else if (resultCode ==
656                                        InputMethodManager.RESULT_UNCHANGED_SHOWN) {
657                                    // If the OSK was already there, focus the form immediately.
658                                    scrollFocusedEditableNodeIntoView();
659                                }
660                            }
661                        };
662                    }
663                }
664        );
665    }
666
667    /**
668     *
669     * @param containerView The view that will act as a container for all views created by this.
670     * @param internalDispatcher Handles dispatching all hidden or super methods to the
671     *                           containerView.
672     * @param nativeWebContents A pointer to the native web contents.
673     * @param windowAndroid An instance of the WindowAndroid.
674     */
675    // Perform important post-construction set up of the ContentViewCore.
676    // We do not require the containing view in the constructor to allow embedders to create a
677    // ContentViewCore without having fully created its containing view. The containing view
678    // is a vital component of the ContentViewCore, so embedders must exercise caution in what
679    // they do with the ContentViewCore before calling initialize().
680    // We supply the nativeWebContents pointer here rather than in the constructor to allow us
681    // to set the private browsing mode at a later point for the WebView implementation.
682    // Note that the caller remains the owner of the nativeWebContents and is responsible for
683    // deleting it after destroying the ContentViewCore.
684    public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
685            long nativeWebContents, WindowAndroid windowAndroid) {
686        mContainerView = containerView;
687        mPositionObserver = new ViewPositionObserver(mContainerView);
688        mPositionListener = new PositionObserver.Listener() {
689            @Override
690            public void onPositionChanged(int x, int y) {
691                if (isSelectionHandleShowing() || isInsertionHandleShowing()) {
692                    temporarilyHideTextHandles();
693                }
694            }
695        };
696
697        long windowNativePointer = windowAndroid != null ? windowAndroid.getNativePointer() : 0;
698
699        long viewAndroidNativePointer = 0;
700        if (windowNativePointer != 0) {
701            mViewAndroid = new ViewAndroid(windowAndroid, getViewAndroidDelegate());
702            viewAndroidNativePointer = mViewAndroid.getNativePointer();
703        }
704
705        mZoomControlsDelegate = new ZoomControlsDelegate() {
706            @Override
707            public void invokeZoomPicker() {}
708            @Override
709            public void dismissZoomPicker() {}
710            @Override
711            public void updateZoomControls() {}
712        };
713
714        mNativeContentViewCore = nativeInit(
715                nativeWebContents, viewAndroidNativePointer, windowNativePointer);
716        mWebContents = nativeGetWebContentsAndroid(mNativeContentViewCore);
717        mContentSettings = new ContentSettings(this, mNativeContentViewCore);
718        initializeContainerView(internalDispatcher);
719
720        mAccessibilityInjector = AccessibilityInjector.newInstance(this);
721
722        String contentDescription = "Web View";
723        if (R.string.accessibility_content_view == 0) {
724            Log.w(TAG, "Setting contentDescription to 'Web View' as no value was specified.");
725        } else {
726            contentDescription = mContext.getResources().getString(
727                    R.string.accessibility_content_view);
728        }
729        mContainerView.setContentDescription(contentDescription);
730        mWebContentsObserver = new WebContentsObserverAndroid(this) {
731            @Override
732            public void didStartLoading(String url) {
733                hidePopupDialog();
734                resetScrollInProgress();
735                resetGestureDetectors();
736            }
737        };
738    }
739
740    @CalledByNative
741    void onNativeContentViewCoreDestroyed(long nativeContentViewCore) {
742        assert nativeContentViewCore == mNativeContentViewCore;
743        mNativeContentViewCore = 0;
744    }
745
746    /**
747     * Set the Container view Internals.
748     * @param internalDispatcher Handles dispatching all hidden or super methods to the
749     *                           containerView.
750     */
751    public void setContainerViewInternals(InternalAccessDelegate internalDispatcher) {
752        mContainerViewInternals = internalDispatcher;
753    }
754
755    /**
756     * Initializes the View that will contain all Views created by the ContentViewCore.
757     *
758     * @param internalDispatcher Handles dispatching all hidden or super methods to the
759     *                           containerView.
760     */
761    private void initializeContainerView(InternalAccessDelegate internalDispatcher) {
762        TraceEvent.begin();
763        mContainerViewInternals = internalDispatcher;
764
765        mContainerView.setWillNotDraw(false);
766        mContainerView.setClickable(true);
767
768        mRenderCoordinates.reset();
769        onRenderCoordinatesUpdated();
770
771        initPopupZoomer(mContext);
772        mImeAdapter = createImeAdapter(mContext);
773        TraceEvent.end();
774    }
775
776    private void initPopupZoomer(Context context) {
777        mPopupZoomer = new PopupZoomer(context);
778        mPopupZoomer.setOnVisibilityChangedListener(new PopupZoomer.OnVisibilityChangedListener() {
779            @Override
780            public void onPopupZoomerShown(final PopupZoomer zoomer) {
781                mContainerView.post(new Runnable() {
782                    @Override
783                    public void run() {
784                        if (mContainerView.indexOfChild(zoomer) == -1) {
785                            mContainerView.addView(zoomer);
786                        } else {
787                            assert false : "PopupZoomer should never be shown without being hidden";
788                        }
789                    }
790                });
791            }
792
793            @Override
794            public void onPopupZoomerHidden(final PopupZoomer zoomer) {
795                mContainerView.post(new Runnable() {
796                    @Override
797                    public void run() {
798                        if (mContainerView.indexOfChild(zoomer) != -1) {
799                            mContainerView.removeView(zoomer);
800                            mContainerView.invalidate();
801                        } else {
802                            assert false : "PopupZoomer should never be hidden without being shown";
803                        }
804                    }
805                });
806            }
807        });
808        // TODO(yongsheng): LONG_TAP is not enabled in PopupZoomer. So need to dispatch a LONG_TAP
809        // gesture if a user completes a tap on PopupZoomer UI after a LONG_PRESS gesture.
810        PopupZoomer.OnTapListener listener = new PopupZoomer.OnTapListener() {
811            @Override
812            public boolean onSingleTap(View v, MotionEvent e) {
813                mContainerView.requestFocus();
814                if (mNativeContentViewCore != 0) {
815                    nativeSingleTap(mNativeContentViewCore, e.getEventTime(), e.getX(), e.getY());
816                }
817                return true;
818            }
819
820            @Override
821            public boolean onLongPress(View v, MotionEvent e) {
822                if (mNativeContentViewCore != 0) {
823                    nativeLongPress(mNativeContentViewCore, e.getEventTime(), e.getX(), e.getY());
824                }
825                return true;
826            }
827        };
828        mPopupZoomer.setOnTapListener(listener);
829    }
830
831    /**
832     * Destroy the internal state of the ContentView. This method may only be
833     * called after the ContentView has been removed from the view system. No
834     * other methods may be called on this ContentView after this method has
835     * been called.
836     */
837    public void destroy() {
838        if (mNativeContentViewCore != 0) {
839            nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore);
840        }
841        mWebContents = null;
842        resetVSyncNotification();
843        mVSyncProvider = null;
844        if (mViewAndroid != null) mViewAndroid.destroy();
845        mNativeContentViewCore = 0;
846        mContentSettings = null;
847        mJavaScriptInterfaces.clear();
848        mRetainedJavaScriptObjects.clear();
849        unregisterAccessibilityContentObserver();
850        mGestureStateListeners.clear();
851        ScreenOrientationListener.getInstance().removeObserver(this);
852    }
853
854    private void unregisterAccessibilityContentObserver() {
855        if (mAccessibilityScriptInjectionObserver == null) {
856            return;
857        }
858        getContext().getContentResolver().unregisterContentObserver(
859                mAccessibilityScriptInjectionObserver);
860        mAccessibilityScriptInjectionObserver = null;
861    }
862
863    /**
864     * Returns true initially, false after destroy() has been called.
865     * It is illegal to call any other public method after destroy().
866     */
867    public boolean isAlive() {
868        return mNativeContentViewCore != 0;
869    }
870
871    /**
872     * This is only useful for passing over JNI to native code that requires ContentViewCore*.
873     * @return native ContentViewCore pointer.
874     */
875    @CalledByNative
876    public long getNativeContentViewCore() {
877        return mNativeContentViewCore;
878    }
879
880    public void setContentViewClient(ContentViewClient client) {
881        if (client == null) {
882            throw new IllegalArgumentException("The client can't be null.");
883        }
884        mContentViewClient = client;
885    }
886
887    ContentViewClient getContentViewClient() {
888        if (mContentViewClient == null) {
889            // We use the Null Object pattern to avoid having to perform a null check in this class.
890            // We create it lazily because most of the time a client will be set almost immediately
891            // after ContentView is created.
892            mContentViewClient = new ContentViewClient();
893            // We don't set the native ContentViewClient pointer here on purpose. The native
894            // implementation doesn't mind a null delegate and using one is better than passing a
895            // Null Object, since we cut down on the number of JNI calls.
896        }
897        return mContentViewClient;
898    }
899
900    public int getBackgroundColor() {
901        if (mNativeContentViewCore != 0) {
902            return nativeGetBackgroundColor(mNativeContentViewCore);
903        }
904        return Color.WHITE;
905    }
906
907    @CalledByNative
908    private void onBackgroundColorChanged(int color) {
909        getContentViewClient().onBackgroundColorChanged(color);
910    }
911
912    /**
913     * Load url without fixing up the url string. Consumers of ContentView are responsible for
914     * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
915     * off during user input).
916     *
917     * @param params Parameters for this load.
918     */
919    public void loadUrl(LoadUrlParams params) {
920        if (mNativeContentViewCore == 0) return;
921
922        nativeLoadUrl(mNativeContentViewCore,
923                params.mUrl,
924                params.mLoadUrlType,
925                params.mTransitionType,
926                params.mUaOverrideOption,
927                params.getExtraHeadersString(),
928                params.mPostData,
929                params.mBaseUrlForDataUrl,
930                params.mVirtualUrlForDataUrl,
931                params.mCanLoadLocalResources);
932    }
933
934    /**
935     * Stops loading the current web contents.
936     */
937    public void stopLoading() {
938        if (mNativeContentViewCore != 0) nativeStopLoading(mNativeContentViewCore);
939    }
940
941    /**
942     * Get the URL of the current page.
943     *
944     * @return The URL of the current page.
945     */
946    public String getUrl() {
947        if (mNativeContentViewCore != 0) return nativeGetURL(mNativeContentViewCore);
948        return null;
949    }
950
951    /**
952     * Get the title of the current page.
953     *
954     * @return The title of the current page.
955     */
956    public String getTitle() {
957        if (mNativeContentViewCore != 0) return nativeGetTitle(mNativeContentViewCore);
958        return null;
959    }
960
961    /**
962     * Shows an interstitial page driven by the passed in delegate.
963     *
964     * @param url The URL being blocked by the interstitial.
965     * @param delegate The delegate handling the interstitial.
966     */
967    @VisibleForTesting
968    public void showInterstitialPage(
969            String url, InterstitialPageDelegateAndroid delegate) {
970        if (mNativeContentViewCore == 0) return;
971        nativeShowInterstitialPage(mNativeContentViewCore, url, delegate.getNative());
972    }
973
974    /**
975     * @return Whether the page is currently showing an interstitial, such as a bad HTTPS page.
976     */
977    public boolean isShowingInterstitialPage() {
978        return mNativeContentViewCore == 0 ?
979                false : nativeIsShowingInterstitialPage(mNativeContentViewCore);
980    }
981
982    /**
983     * Mark any new frames that have arrived since this function was last called as non-pending.
984     *
985     * @return Whether there was a pending frame from the renderer.
986     */
987    public boolean consumePendingRendererFrame() {
988        boolean hadPendingFrame = mPendingRendererFrame;
989        mPendingRendererFrame = false;
990        return hadPendingFrame;
991    }
992
993    /**
994     * @return Viewport width in physical pixels as set from onSizeChanged.
995     */
996    @CalledByNative
997    public int getViewportWidthPix() { return mViewportWidthPix; }
998
999    /**
1000     * @return Viewport height in physical pixels as set from onSizeChanged.
1001     */
1002    @CalledByNative
1003    public int getViewportHeightPix() { return mViewportHeightPix; }
1004
1005    /**
1006     * @return Width of underlying physical surface.
1007     */
1008    @CalledByNative
1009    public int getPhysicalBackingWidthPix() { return mPhysicalBackingWidthPix; }
1010
1011    /**
1012     * @return Height of underlying physical surface.
1013     */
1014    @CalledByNative
1015    public int getPhysicalBackingHeightPix() { return mPhysicalBackingHeightPix; }
1016
1017    /**
1018     * @return Amount the output surface extends past the bottom of the window viewport.
1019     */
1020    @CalledByNative
1021    public int getOverdrawBottomHeightPix() { return mOverdrawBottomHeightPix; }
1022
1023    /**
1024     * @return The amount to shrink the viewport relative to {@link #getViewportWidthPix()}.
1025     */
1026    @CalledByNative
1027    public int getViewportSizeOffsetWidthPix() { return mViewportSizeOffsetWidthPix; }
1028
1029    /**
1030     * @return The amount to shrink the viewport relative to {@link #getViewportHeightPix()}.
1031     */
1032    @CalledByNative
1033    public int getViewportSizeOffsetHeightPix() { return mViewportSizeOffsetHeightPix; }
1034
1035    /**
1036     * @see android.webkit.WebView#getContentHeight()
1037     */
1038    public float getContentHeightCss() {
1039        return mRenderCoordinates.getContentHeightCss();
1040    }
1041
1042    /**
1043     * @see android.webkit.WebView#getContentWidth()
1044     */
1045    public float getContentWidthCss() {
1046        return mRenderCoordinates.getContentWidthCss();
1047    }
1048
1049    // TODO(teddchoc): Remove all these navigation controller methods from here and have the
1050    //                 embedders manage it.
1051    /**
1052     * @return Whether the current WebContents has a previous navigation entry.
1053     */
1054    public boolean canGoBack() {
1055        return mWebContents != null && mWebContents.getNavigationController().canGoBack();
1056    }
1057
1058    /**
1059     * @return Whether the current WebContents has a navigation entry after the current one.
1060     */
1061    public boolean canGoForward() {
1062        return mWebContents != null && mWebContents.getNavigationController().canGoForward();
1063    }
1064
1065    /**
1066     * @param offset The offset into the navigation history.
1067     * @return Whether we can move in history by given offset
1068     */
1069    public boolean canGoToOffset(int offset) {
1070        return mWebContents != null &&
1071                mWebContents.getNavigationController().canGoToOffset(offset);
1072    }
1073
1074    /**
1075     * Navigates to the specified offset from the "current entry". Does nothing if the offset is out
1076     * of bounds.
1077     * @param offset The offset into the navigation history.
1078     */
1079    public void goToOffset(int offset) {
1080        if (mWebContents != null) mWebContents.getNavigationController().goToOffset(offset);
1081    }
1082
1083    @Override
1084    public void goToNavigationIndex(int index) {
1085        if (mWebContents != null) {
1086            mWebContents.getNavigationController().goToNavigationIndex(index);
1087        }
1088    }
1089
1090    /**
1091     * Goes to the navigation entry before the current one.
1092     */
1093    public void goBack() {
1094        if (mWebContents != null) mWebContents.getNavigationController().goBack();
1095    }
1096
1097    /**
1098     * Goes to the navigation entry following the current one.
1099     */
1100    public void goForward() {
1101        if (mWebContents != null) mWebContents.getNavigationController().goForward();
1102    }
1103
1104    /**
1105     * Loads the current navigation if there is a pending lazy load (after tab restore).
1106     */
1107    public void loadIfNecessary() {
1108        if (mNativeContentViewCore != 0) nativeLoadIfNecessary(mNativeContentViewCore);
1109    }
1110
1111    /**
1112     * Requests the current navigation to be loaded upon the next call to loadIfNecessary().
1113     */
1114    public void requestRestoreLoad() {
1115        if (mNativeContentViewCore != 0) nativeRequestRestoreLoad(mNativeContentViewCore);
1116    }
1117
1118    /**
1119     * Reload the current page.
1120     */
1121    public void reload(boolean checkForRepost) {
1122        mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1123        if (mNativeContentViewCore != 0) {
1124            nativeReload(mNativeContentViewCore, checkForRepost);
1125        }
1126    }
1127
1128    /**
1129     * Reload the current page, ignoring the contents of the cache.
1130     */
1131    public void reloadIgnoringCache(boolean checkForRepost) {
1132        mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1133        if (mNativeContentViewCore != 0) {
1134            nativeReloadIgnoringCache(mNativeContentViewCore, checkForRepost);
1135        }
1136    }
1137
1138    /**
1139     * Cancel the pending reload.
1140     */
1141    public void cancelPendingReload() {
1142        if (mNativeContentViewCore != 0) nativeCancelPendingReload(mNativeContentViewCore);
1143    }
1144
1145    /**
1146     * Continue the pending reload.
1147     */
1148    public void continuePendingReload() {
1149        if (mNativeContentViewCore != 0) nativeContinuePendingReload(mNativeContentViewCore);
1150    }
1151
1152    /**
1153     * Clears the ContentViewCore's page history in both the backwards and
1154     * forwards directions.
1155     */
1156    public void clearHistory() {
1157        if (mNativeContentViewCore != 0) nativeClearHistory(mNativeContentViewCore);
1158    }
1159
1160    /**
1161     * @return The selected text (empty if no text selected).
1162     */
1163    public String getSelectedText() {
1164        return mHasSelection ? mLastSelectedText : "";
1165    }
1166
1167    /**
1168     * @return Whether the current selection is editable (false if no text selected).
1169     */
1170    public boolean isSelectionEditable() {
1171        return mHasSelection ? mSelectionEditable : false;
1172    }
1173
1174    // End FrameLayout overrides.
1175
1176    /**
1177     * @see View#onTouchEvent(MotionEvent)
1178     */
1179    public boolean onTouchEvent(MotionEvent event) {
1180        cancelRequestToScrollFocusedEditableNodeIntoView();
1181
1182        if (!mRequestedVSyncForInput) {
1183            mRequestedVSyncForInput = true;
1184            addVSyncSubscriber();
1185        }
1186
1187        final int eventAction = event.getActionMasked();
1188
1189        // Only these actions have any effect on gesture detection.  Other
1190        // actions have no corresponding WebTouchEvent type and may confuse the
1191        // touch pipline, so we ignore them entirely.
1192        if (eventAction != MotionEvent.ACTION_DOWN
1193                && eventAction != MotionEvent.ACTION_UP
1194                && eventAction != MotionEvent.ACTION_CANCEL
1195                && eventAction != MotionEvent.ACTION_MOVE
1196                && eventAction != MotionEvent.ACTION_POINTER_DOWN
1197                && eventAction != MotionEvent.ACTION_POINTER_UP) {
1198            return false;
1199        }
1200
1201        if (mNativeContentViewCore == 0) return false;
1202        final int pointerCount = event.getPointerCount();
1203        return nativeOnTouchEvent(mNativeContentViewCore, event,
1204                event.getEventTime(), eventAction,
1205                pointerCount, event.getHistorySize(), event.getActionIndex(),
1206                event.getX(), event.getY(),
1207                pointerCount > 1 ? event.getX(1) : 0,
1208                pointerCount > 1 ? event.getY(1) : 0,
1209                event.getPointerId(0), pointerCount > 1 ? event.getPointerId(1) : -1,
1210                event.getTouchMajor(), pointerCount > 1 ? event.getTouchMajor(1) : 0);
1211    }
1212
1213    public void setIgnoreRemainingTouchEvents() {
1214        if (mNativeContentViewCore == 0) return;
1215        nativeIgnoreRemainingTouchEvents(mNativeContentViewCore);
1216    }
1217
1218    public boolean isScrollInProgress() {
1219        return mTouchScrollInProgress || mPotentiallyActiveFlingCount > 0;
1220    }
1221
1222    @SuppressWarnings("unused")
1223    @CalledByNative
1224    private void onFlingStartEventConsumed(int vx, int vy) {
1225        mTouchScrollInProgress = false;
1226        mPotentiallyActiveFlingCount++;
1227        temporarilyHideTextHandles();
1228        for (mGestureStateListenersIterator.rewind();
1229                    mGestureStateListenersIterator.hasNext();) {
1230            mGestureStateListenersIterator.next().onFlingStartGesture(
1231                    vx, vy, computeVerticalScrollOffset(), computeVerticalScrollExtent());
1232        }
1233    }
1234
1235    @SuppressWarnings("unused")
1236    @CalledByNative
1237    private void onFlingStartEventHadNoConsumer(int vx, int vy) {
1238        mTouchScrollInProgress = false;
1239        for (mGestureStateListenersIterator.rewind();
1240                    mGestureStateListenersIterator.hasNext();) {
1241            mGestureStateListenersIterator.next().onUnhandledFlingStartEvent(vx, vy);
1242        }
1243    }
1244
1245    @SuppressWarnings("unused")
1246    @CalledByNative
1247    private void onFlingCancelEventAck() {
1248        updateGestureStateListener(GestureEventType.FLING_CANCEL);
1249    }
1250
1251    @SuppressWarnings("unused")
1252    @CalledByNative
1253    private void onScrollBeginEventAck() {
1254        mTouchScrollInProgress = true;
1255        temporarilyHideTextHandles();
1256        mZoomControlsDelegate.invokeZoomPicker();
1257        updateGestureStateListener(GestureEventType.SCROLL_START);
1258    }
1259
1260    @SuppressWarnings("unused")
1261    @CalledByNative
1262    private void onScrollUpdateGestureConsumed() {
1263        mZoomControlsDelegate.invokeZoomPicker();
1264        for (mGestureStateListenersIterator.rewind();
1265                mGestureStateListenersIterator.hasNext();) {
1266            mGestureStateListenersIterator.next().onScrollUpdateGestureConsumed();
1267        }
1268    }
1269
1270    @SuppressWarnings("unused")
1271    @CalledByNative
1272    private void onScrollEndEventAck() {
1273        if (!mTouchScrollInProgress) return;
1274        mTouchScrollInProgress = false;
1275        updateGestureStateListener(GestureEventType.SCROLL_END);
1276    }
1277
1278    @SuppressWarnings("unused")
1279    @CalledByNative
1280    private void onPinchBeginEventAck() {
1281        temporarilyHideTextHandles();
1282        updateGestureStateListener(GestureEventType.PINCH_BEGIN);
1283    }
1284
1285    @SuppressWarnings("unused")
1286    @CalledByNative
1287    private void onPinchEndEventAck() {
1288        updateGestureStateListener(GestureEventType.PINCH_END);
1289    }
1290
1291    @SuppressWarnings("unused")
1292    @CalledByNative
1293    private void onDoubleTapEventAck() {
1294        temporarilyHideTextHandles();
1295    }
1296
1297    /**
1298     * Called just prior to a tap or press gesture being forwarded to the renderer.
1299     */
1300    @SuppressWarnings("unused")
1301    @CalledByNative
1302    private boolean filterTapOrPressEvent(int type, int x, int y) {
1303        if (type == GestureEventType.LONG_PRESS && offerLongPressToEmbedder()) {
1304            return true;
1305        }
1306        updateForTapOrPress(type, x, y);
1307        return false;
1308    }
1309
1310    @VisibleForTesting
1311    public void sendDoubleTapForTest(long timeMs, int x, int y) {
1312        if (mNativeContentViewCore == 0) return;
1313        nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1314    }
1315
1316    @VisibleForTesting
1317    public void flingForTest(long timeMs, int x, int y, int velocityX, int velocityY) {
1318        if (mNativeContentViewCore == 0) return;
1319        nativeFlingCancel(mNativeContentViewCore, timeMs);
1320        nativeScrollBegin(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1321        nativeFlingStart(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1322    }
1323
1324    /**
1325     * Add a listener that gets alerted on gesture state changes.
1326     * @param listener Listener to add.
1327     */
1328    public void addGestureStateListener(GestureStateListener listener) {
1329        mGestureStateListeners.addObserver(listener);
1330    }
1331
1332    /**
1333     * Removes a listener that was added to watch for gesture state changes.
1334     * @param listener Listener to remove.
1335     */
1336    public void removeGestureStateListener(GestureStateListener listener) {
1337        mGestureStateListeners.removeObserver(listener);
1338    }
1339
1340    void updateGestureStateListener(int gestureType) {
1341        for (mGestureStateListenersIterator.rewind();
1342                mGestureStateListenersIterator.hasNext();) {
1343            GestureStateListener listener = mGestureStateListenersIterator.next();
1344            switch (gestureType) {
1345                case GestureEventType.PINCH_BEGIN:
1346                    listener.onPinchStarted();
1347                    break;
1348                case GestureEventType.PINCH_END:
1349                    listener.onPinchEnded();
1350                    break;
1351                case GestureEventType.FLING_END:
1352                    listener.onFlingEndGesture(
1353                            computeVerticalScrollOffset(),
1354                            computeVerticalScrollExtent());
1355                    break;
1356                case GestureEventType.FLING_CANCEL:
1357                    listener.onFlingCancelGesture();
1358                    break;
1359                case GestureEventType.SCROLL_START:
1360                    listener.onScrollStarted(
1361                            computeVerticalScrollOffset(),
1362                            computeVerticalScrollExtent());
1363                    break;
1364                case GestureEventType.SCROLL_END:
1365                    listener.onScrollEnded(
1366                            computeVerticalScrollOffset(),
1367                            computeVerticalScrollExtent());
1368                    break;
1369                default:
1370                    break;
1371            }
1372        }
1373    }
1374
1375    /** Callback interface for evaluateJavaScript(). */
1376    public interface JavaScriptCallback {
1377        void handleJavaScriptResult(String jsonResult);
1378    }
1379
1380    /**
1381     * Injects the passed Javascript code in the current page and evaluates it.
1382     * If a result is required, pass in a callback.
1383     * Used in automation tests.
1384     *
1385     * @param script The Javascript to execute.
1386     * @param callback The callback to be fired off when a result is ready. The script's
1387     *                 result will be json encoded and passed as the parameter, and the call
1388     *                 will be made on the main thread.
1389     *                 If no result is required, pass null.
1390     */
1391    public void evaluateJavaScript(String script, JavaScriptCallback callback) {
1392        if (mNativeContentViewCore == 0) return;
1393        nativeEvaluateJavaScript(mNativeContentViewCore, script, callback, false);
1394    }
1395
1396    /**
1397     * Injects the passed Javascript code in the current page and evaluates it.
1398     * If there is no page existing, a new one will be created.
1399     *
1400     * @param script The Javascript to execute.
1401     */
1402    public void evaluateJavaScriptEvenIfNotYetNavigated(String script) {
1403        if (mNativeContentViewCore == 0) return;
1404        nativeEvaluateJavaScript(mNativeContentViewCore, script, null, true);
1405    }
1406
1407    /**
1408     * To be called when the ContentView is shown.
1409     */
1410    public void onShow() {
1411        assert mNativeContentViewCore != 0;
1412        if (!mInForeground) {
1413            ChildProcessLauncher.getBindingManager().setInForeground(getCurrentRenderProcessId(),
1414                    true);
1415        }
1416        mInForeground = true;
1417        nativeOnShow(mNativeContentViewCore);
1418        setAccessibilityState(mAccessibilityManager.isEnabled());
1419    }
1420
1421    /**
1422     * @return The ID of the renderer process that backs this tab or
1423     *         {@link #INVALID_RENDER_PROCESS_PID} if there is none.
1424     */
1425    public int getCurrentRenderProcessId() {
1426        return nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1427    }
1428
1429    /**
1430     * To be called when the ContentView is hidden.
1431     */
1432    public void onHide() {
1433        assert mNativeContentViewCore != 0;
1434        if (mInForeground) {
1435            ChildProcessLauncher.getBindingManager().setInForeground(getCurrentRenderProcessId(),
1436                    false);
1437        }
1438        mInForeground = false;
1439        hidePopupDialog();
1440        setInjectedAccessibility(false);
1441        nativeOnHide(mNativeContentViewCore);
1442    }
1443
1444    /**
1445     * Return the ContentSettings object used to retrieve the settings for this
1446     * ContentViewCore. For modifications, ChromeNativePreferences is to be used.
1447     * @return A ContentSettings object that can be used to retrieve this
1448     *         ContentViewCore's settings.
1449     */
1450    public ContentSettings getContentSettings() {
1451        return mContentSettings;
1452    }
1453
1454    private void onRenderCoordinatesUpdated() {
1455        if (mNativeContentViewCore == 0) return;
1456
1457        // We disable double tap zoom for pages that have a width=device-width
1458        // or narrower viewport (indicating that this is a mobile-optimized or
1459        // responsive web design, so text will be legible without zooming).
1460        // We also disable it for pages that disallow the user from zooming in
1461        // or out (even if they don't have a device-width or narrower viewport).
1462        nativeSetDoubleTapSupportForPageEnabled(mNativeContentViewCore,
1463                !mRenderCoordinates.hasMobileViewport() && !mRenderCoordinates.hasFixedPageScale());
1464    }
1465
1466    private void hidePopupDialog() {
1467        if (mSelectPopupDialog != null) {
1468            mSelectPopupDialog.hide();
1469            mSelectPopupDialog = null;
1470        }
1471        hideHandles();
1472        hideSelectActionBar();
1473    }
1474
1475    void hideSelectActionBar() {
1476        if (mActionMode != null) {
1477            mActionMode.finish();
1478            mActionMode = null;
1479        }
1480    }
1481
1482    public boolean isSelectActionBarShowing() {
1483        return mActionMode != null;
1484    }
1485
1486    private void resetGestureDetectors() {
1487        if (mNativeContentViewCore == 0) return;
1488        nativeResetGestureDetectors(mNativeContentViewCore);
1489    }
1490
1491    /**
1492     * @see View#onAttachedToWindow()
1493     */
1494    @SuppressWarnings("javadoc")
1495    public void onAttachedToWindow() {
1496        setAccessibilityState(mAccessibilityManager.isEnabled());
1497
1498        ScreenOrientationListener.getInstance().addObserver(this, mContext);
1499    }
1500
1501    /**
1502     * @see View#onDetachedFromWindow()
1503     */
1504    @SuppressWarnings("javadoc")
1505    @SuppressLint("MissingSuperCall")
1506    public void onDetachedFromWindow() {
1507        setInjectedAccessibility(false);
1508        hidePopupDialog();
1509        mZoomControlsDelegate.dismissZoomPicker();
1510        unregisterAccessibilityContentObserver();
1511
1512        ScreenOrientationListener.getInstance().removeObserver(this);
1513    }
1514
1515    /**
1516     * @see View#onVisibilityChanged(android.view.View, int)
1517     */
1518    public void onVisibilityChanged(View changedView, int visibility) {
1519        if (visibility != View.VISIBLE) {
1520            mZoomControlsDelegate.dismissZoomPicker();
1521        }
1522    }
1523
1524    /**
1525     * @see View#onCreateInputConnection(EditorInfo)
1526     */
1527    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1528        if (!mImeAdapter.hasTextInputType()) {
1529            // Although onCheckIsTextEditor will return false in this case, the EditorInfo
1530            // is still used by the InputMethodService. Need to make sure the IME doesn't
1531            // enter fullscreen mode.
1532            outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
1533        }
1534        mInputConnection = mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter,
1535                mEditable, outAttrs);
1536        return mInputConnection;
1537    }
1538
1539    @VisibleForTesting
1540    public AdapterInputConnection getAdapterInputConnectionForTest() {
1541        return mInputConnection;
1542    }
1543
1544    @VisibleForTesting
1545    public Editable getEditableForTest() {
1546        return mEditable;
1547    }
1548
1549    /**
1550     * @see View#onCheckIsTextEditor()
1551     */
1552    public boolean onCheckIsTextEditor() {
1553        return mImeAdapter.hasTextInputType();
1554    }
1555
1556    /**
1557     * @see View#onConfigurationChanged(Configuration)
1558     */
1559    @SuppressWarnings("javadoc")
1560    public void onConfigurationChanged(Configuration newConfig) {
1561        TraceEvent.begin();
1562
1563        if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
1564            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
1565                    ImeAdapter.getTextInputTypeNone());
1566            mInputMethodManagerWrapper.restartInput(mContainerView);
1567        }
1568        mContainerViewInternals.super_onConfigurationChanged(newConfig);
1569
1570        // To request layout has side effect, but it seems OK as it only happen in
1571        // onConfigurationChange and layout has to be changed in most case.
1572        mContainerView.requestLayout();
1573        TraceEvent.end();
1574    }
1575
1576    /**
1577     * @see View#onSizeChanged(int, int, int, int)
1578     */
1579    @SuppressWarnings("javadoc")
1580    public void onSizeChanged(int wPix, int hPix, int owPix, int ohPix) {
1581        if (getViewportWidthPix() == wPix && getViewportHeightPix() == hPix) return;
1582
1583        mViewportWidthPix = wPix;
1584        mViewportHeightPix = hPix;
1585        if (mNativeContentViewCore != 0) {
1586            nativeWasResized(mNativeContentViewCore);
1587        }
1588
1589        updateAfterSizeChanged();
1590    }
1591
1592    /**
1593     * Called when the ContentView's position in the activity window changed. This information is
1594     * used for cropping screenshots.
1595     */
1596    public void onLocationInWindowChanged(int x, int y) {
1597        mLocationInWindowX = x;
1598        mLocationInWindowY = y;
1599    }
1600
1601    /**
1602     * Called when the underlying surface the compositor draws to changes size.
1603     * This may be larger than the viewport size.
1604     */
1605    public void onPhysicalBackingSizeChanged(int wPix, int hPix) {
1606        if (mPhysicalBackingWidthPix == wPix && mPhysicalBackingHeightPix == hPix) return;
1607
1608        mPhysicalBackingWidthPix = wPix;
1609        mPhysicalBackingHeightPix = hPix;
1610
1611        if (mNativeContentViewCore != 0) {
1612            nativeWasResized(mNativeContentViewCore);
1613        }
1614    }
1615
1616    /**
1617     * Called when the amount the surface is overdrawing off the bottom has changed.
1618     * @param overdrawHeightPix The overdraw height.
1619     */
1620    public void onOverdrawBottomHeightChanged(int overdrawHeightPix) {
1621        if (mOverdrawBottomHeightPix == overdrawHeightPix) return;
1622
1623        mOverdrawBottomHeightPix = overdrawHeightPix;
1624
1625        if (mNativeContentViewCore != 0) {
1626            nativeWasResized(mNativeContentViewCore);
1627        }
1628    }
1629
1630    private void updateAfterSizeChanged() {
1631        mPopupZoomer.hide(false);
1632
1633        // Execute a delayed form focus operation because the OSK was brought
1634        // up earlier.
1635        if (!mFocusPreOSKViewportRect.isEmpty()) {
1636            Rect rect = new Rect();
1637            getContainerView().getWindowVisibleDisplayFrame(rect);
1638            if (!rect.equals(mFocusPreOSKViewportRect)) {
1639                // Only assume the OSK triggered the onSizeChanged if width was preserved.
1640                if (rect.width() == mFocusPreOSKViewportRect.width()) {
1641                    scrollFocusedEditableNodeIntoView();
1642                }
1643                cancelRequestToScrollFocusedEditableNodeIntoView();
1644            }
1645        }
1646    }
1647
1648    private void cancelRequestToScrollFocusedEditableNodeIntoView() {
1649        // Zero-ing the rect will prevent |updateAfterSizeChanged()| from
1650        // issuing the delayed form focus event.
1651        mFocusPreOSKViewportRect.setEmpty();
1652    }
1653
1654    private void scrollFocusedEditableNodeIntoView() {
1655        if (mNativeContentViewCore == 0) return;
1656        // The native side keeps track of whether the zoom and scroll actually occurred. It is
1657        // more efficient to do it this way and sometimes fire an unnecessary message rather
1658        // than synchronize with the renderer and always have an additional message.
1659        nativeScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1660    }
1661
1662    /**
1663     * @see View#onWindowFocusChanged(boolean)
1664     */
1665    public void onWindowFocusChanged(boolean hasWindowFocus) {
1666        if (!hasWindowFocus) {
1667            if (mNativeContentViewCore == 0) return;
1668            nativeOnWindowFocusLost(mNativeContentViewCore);
1669        }
1670    }
1671
1672    public void onFocusChanged(boolean gainFocus) {
1673        if (!gainFocus) {
1674            hideImeIfNeeded();
1675        }
1676        if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1677    }
1678
1679    /**
1680     * @see View#onKeyUp(int, KeyEvent)
1681     */
1682    public boolean onKeyUp(int keyCode, KeyEvent event) {
1683        if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1684            mPopupZoomer.hide(true);
1685            return true;
1686        }
1687        return mContainerViewInternals.super_onKeyUp(keyCode, event);
1688    }
1689
1690    /**
1691     * @see View#dispatchKeyEventPreIme(KeyEvent)
1692     */
1693    public boolean dispatchKeyEventPreIme(KeyEvent event) {
1694        try {
1695            TraceEvent.begin();
1696            return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1697        } finally {
1698            TraceEvent.end();
1699        }
1700    }
1701
1702    /**
1703     * @see View#dispatchKeyEvent(KeyEvent)
1704     */
1705    public boolean dispatchKeyEvent(KeyEvent event) {
1706        if (getContentViewClient().shouldOverrideKeyEvent(event)) {
1707            return mContainerViewInternals.super_dispatchKeyEvent(event);
1708        }
1709
1710        if (mImeAdapter.dispatchKeyEvent(event)) return true;
1711
1712        return mContainerViewInternals.super_dispatchKeyEvent(event);
1713    }
1714
1715    /**
1716     * @see View#onHoverEvent(MotionEvent)
1717     * Mouse move events are sent on hover enter, hover move and hover exit.
1718     * They are sent on hover exit because sometimes it acts as both a hover
1719     * move and hover exit.
1720     */
1721    public boolean onHoverEvent(MotionEvent event) {
1722        TraceEvent.begin("onHoverEvent");
1723
1724        if (mBrowserAccessibilityManager != null) {
1725            return mBrowserAccessibilityManager.onHoverEvent(event);
1726        }
1727
1728        // Work around Android bug where the x, y coordinates of a hover exit
1729        // event are incorrect when touch exploration is on.
1730        if (mTouchExplorationEnabled && event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
1731            return true;
1732        }
1733
1734        mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1735        if (mNativeContentViewCore != 0) {
1736            nativeSendMouseMoveEvent(mNativeContentViewCore, event.getEventTime(),
1737                    event.getX(), event.getY());
1738        }
1739        TraceEvent.end("onHoverEvent");
1740        return true;
1741    }
1742
1743    /**
1744     * @see View#onGenericMotionEvent(MotionEvent)
1745     */
1746    public boolean onGenericMotionEvent(MotionEvent event) {
1747        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1748            switch (event.getAction()) {
1749                case MotionEvent.ACTION_SCROLL:
1750                    nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
1751                            event.getX(), event.getY(),
1752                            event.getAxisValue(MotionEvent.AXIS_VSCROLL));
1753
1754                    mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1755                    // Send a delayed onMouseMove event so that we end
1756                    // up hovering over the right position after the scroll.
1757                    final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event);
1758                    mFakeMouseMoveRunnable = new Runnable() {
1759                        @Override
1760                        public void run() {
1761                            onHoverEvent(eventFakeMouseMove);
1762                        }
1763                    };
1764                    mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1765                    return true;
1766            }
1767        }
1768        return mContainerViewInternals.super_onGenericMotionEvent(event);
1769    }
1770
1771    /**
1772     * @see View#scrollBy(int, int)
1773     * Currently the ContentView scrolling happens in the native side. In
1774     * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
1775     * are overridden, so that View's mScrollX and mScrollY will be unchanged at
1776     * (0, 0). This is critical for drawing ContentView correctly.
1777     */
1778    public void scrollBy(int xPix, int yPix) {
1779        if (mNativeContentViewCore != 0) {
1780            nativeScrollBy(mNativeContentViewCore,
1781                    SystemClock.uptimeMillis(), 0, 0, xPix, yPix);
1782        }
1783    }
1784
1785    /**
1786     * @see View#scrollTo(int, int)
1787     */
1788    public void scrollTo(int xPix, int yPix) {
1789        if (mNativeContentViewCore == 0) return;
1790        final float xCurrentPix = mRenderCoordinates.getScrollXPix();
1791        final float yCurrentPix = mRenderCoordinates.getScrollYPix();
1792        final float dxPix = xPix - xCurrentPix;
1793        final float dyPix = yPix - yCurrentPix;
1794        if (dxPix != 0 || dyPix != 0) {
1795            long time = SystemClock.uptimeMillis();
1796            nativeScrollBegin(mNativeContentViewCore, time,
1797                    xCurrentPix, yCurrentPix, -dxPix, -dyPix);
1798            nativeScrollBy(mNativeContentViewCore,
1799                    time, xCurrentPix, yCurrentPix, dxPix, dyPix);
1800            nativeScrollEnd(mNativeContentViewCore, time);
1801        }
1802    }
1803
1804    // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1805    //       see: b/6029133
1806    public int getNativeScrollXForTest() {
1807        return mRenderCoordinates.getScrollXPixInt();
1808    }
1809
1810    // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1811    //       see: b/6029133
1812    public int getNativeScrollYForTest() {
1813        return mRenderCoordinates.getScrollYPixInt();
1814    }
1815
1816    /**
1817     * @see View#computeHorizontalScrollExtent()
1818     */
1819    @SuppressWarnings("javadoc")
1820    public int computeHorizontalScrollExtent() {
1821        return mRenderCoordinates.getLastFrameViewportWidthPixInt();
1822    }
1823
1824    /**
1825     * @see View#computeHorizontalScrollOffset()
1826     */
1827    @SuppressWarnings("javadoc")
1828    public int computeHorizontalScrollOffset() {
1829        return mRenderCoordinates.getScrollXPixInt();
1830    }
1831
1832    /**
1833     * @see View#computeHorizontalScrollRange()
1834     */
1835    @SuppressWarnings("javadoc")
1836    public int computeHorizontalScrollRange() {
1837        return mRenderCoordinates.getContentWidthPixInt();
1838    }
1839
1840    /**
1841     * @see View#computeVerticalScrollExtent()
1842     */
1843    @SuppressWarnings("javadoc")
1844    public int computeVerticalScrollExtent() {
1845        return mRenderCoordinates.getLastFrameViewportHeightPixInt();
1846    }
1847
1848    /**
1849     * @see View#computeVerticalScrollOffset()
1850     */
1851    @SuppressWarnings("javadoc")
1852    public int computeVerticalScrollOffset() {
1853        return mRenderCoordinates.getScrollYPixInt();
1854    }
1855
1856    /**
1857     * @see View#computeVerticalScrollRange()
1858     */
1859    @SuppressWarnings("javadoc")
1860    public int computeVerticalScrollRange() {
1861        return mRenderCoordinates.getContentHeightPixInt();
1862    }
1863
1864    // End FrameLayout overrides.
1865
1866    /**
1867     * @see View#awakenScrollBars(int, boolean)
1868     */
1869    @SuppressWarnings("javadoc")
1870    public boolean awakenScrollBars(int startDelay, boolean invalidate) {
1871        // For the default implementation of ContentView which draws the scrollBars on the native
1872        // side, calling this function may get us into a bad state where we keep drawing the
1873        // scrollBars, so disable it by always returning false.
1874        if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
1875            return false;
1876        } else {
1877            return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
1878        }
1879    }
1880
1881    private void updateForTapOrPress(int type, float xPix, float yPix) {
1882        if (type != GestureEventType.SINGLE_TAP_CONFIRMED
1883                && type != GestureEventType.SINGLE_TAP_UP
1884                && type != GestureEventType.LONG_PRESS
1885                && type != GestureEventType.LONG_TAP) {
1886            return;
1887        }
1888
1889        if (mContainerView.isFocusable() && mContainerView.isFocusableInTouchMode()
1890                && !mContainerView.isFocused())  {
1891            mContainerView.requestFocus();
1892        }
1893
1894        if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
1895
1896        if (type == GestureEventType.LONG_PRESS
1897                || type == GestureEventType.LONG_TAP) {
1898            getInsertionHandleController().allowAutomaticShowing();
1899            getSelectionHandleController().allowAutomaticShowing();
1900        } else {
1901            setClickXAndY((int) xPix, (int) yPix);
1902            if (mSelectionEditable) getInsertionHandleController().allowAutomaticShowing();
1903        }
1904    }
1905
1906    private void setClickXAndY(int x, int y) {
1907        mSingleTapX = x;
1908        mSingleTapY = y;
1909    }
1910
1911    /**
1912     * @return The x coordinate for the last point that a singleTap gesture was initiated from.
1913     */
1914    public int getSingleTapX()  {
1915        return mSingleTapX;
1916    }
1917
1918    /**
1919     * @return The y coordinate for the last point that a singleTap gesture was initiated from.
1920     */
1921    public int getSingleTapY()  {
1922        return mSingleTapY;
1923    }
1924
1925    public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
1926        mZoomControlsDelegate = zoomControlsDelegate;
1927    }
1928
1929    public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
1930        if (mNativeContentViewCore == 0) return;
1931        nativeSetMultiTouchZoomSupportEnabled(mNativeContentViewCore, supportsMultiTouchZoom);
1932    }
1933
1934    public void updateDoubleTapSupport(boolean supportsDoubleTap) {
1935        if (mNativeContentViewCore == 0) return;
1936        nativeSetDoubleTapSupportEnabled(mNativeContentViewCore, supportsDoubleTap);
1937    }
1938
1939    public void selectPopupMenuItems(int[] indices) {
1940        if (mNativeContentViewCore != 0) {
1941            nativeSelectPopupMenuItems(mNativeContentViewCore, indices);
1942        }
1943        mSelectPopupDialog = null;
1944    }
1945
1946    /**
1947     * Send the screen orientation value to the renderer.
1948     */
1949    @VisibleForTesting
1950    void sendOrientationChangeEvent(int orientation) {
1951        if (mNativeContentViewCore == 0) return;
1952
1953        nativeSendOrientationChangeEvent(mNativeContentViewCore, orientation);
1954    }
1955
1956    /**
1957     * Register the delegate to be used when content can not be handled by
1958     * the rendering engine, and should be downloaded instead. This will replace
1959     * the current delegate, if any.
1960     * @param delegate An implementation of ContentViewDownloadDelegate.
1961     */
1962    public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
1963        mDownloadDelegate = delegate;
1964    }
1965
1966    // Called by DownloadController.
1967    ContentViewDownloadDelegate getDownloadDelegate() {
1968        return mDownloadDelegate;
1969    }
1970
1971    private SelectionHandleController getSelectionHandleController() {
1972        if (mSelectionHandleController == null) {
1973            mSelectionHandleController = new SelectionHandleController(
1974                    getContainerView(), mPositionObserver) {
1975                @Override
1976                public void selectBetweenCoordinates(int x1, int y1, int x2, int y2) {
1977                    if (mNativeContentViewCore != 0 && !(x1 == x2 && y1 == y2)) {
1978                        nativeSelectBetweenCoordinates(mNativeContentViewCore,
1979                                x1, y1 - mRenderCoordinates.getContentOffsetYPix(),
1980                                x2, y2 - mRenderCoordinates.getContentOffsetYPix());
1981                    }
1982                }
1983
1984                @Override
1985                public void showHandles(int startDir, int endDir) {
1986                    super.showHandles(startDir, endDir);
1987                    showSelectActionBar();
1988                }
1989
1990            };
1991
1992            mSelectionHandleController.hideAndDisallowAutomaticShowing();
1993            updateInsertionSelectionVisibleBounds();
1994        }
1995
1996        return mSelectionHandleController;
1997    }
1998
1999    private InsertionHandleController getInsertionHandleController() {
2000        if (mInsertionHandleController == null) {
2001            mInsertionHandleController = new InsertionHandleController(
2002                    getContainerView(), mPositionObserver) {
2003                private static final int AVERAGE_LINE_HEIGHT = 14;
2004
2005                @Override
2006                public void setCursorPosition(int x, int y) {
2007                    if (mNativeContentViewCore != 0) {
2008                        nativeMoveCaret(mNativeContentViewCore,
2009                                x, y - mRenderCoordinates.getContentOffsetYPix());
2010                    }
2011                }
2012
2013                @Override
2014                public void paste() {
2015                    mImeAdapter.paste();
2016                    hideHandles();
2017                }
2018
2019                @Override
2020                public int getLineHeight() {
2021                    return (int) Math.ceil(
2022                            mRenderCoordinates.fromLocalCssToPix(AVERAGE_LINE_HEIGHT));
2023                }
2024
2025                @Override
2026                public void showHandle() {
2027                    super.showHandle();
2028                }
2029            };
2030
2031            mInsertionHandleController.hideAndDisallowAutomaticShowing();
2032            updateInsertionSelectionVisibleBounds();
2033        }
2034
2035        return mInsertionHandleController;
2036    }
2037
2038    @VisibleForTesting
2039    public InsertionHandleController getInsertionHandleControllerForTest() {
2040        return mInsertionHandleController;
2041    }
2042
2043    @VisibleForTesting
2044    public SelectionHandleController getSelectionHandleControllerForTest() {
2045        return mSelectionHandleController;
2046    }
2047
2048    private void updateHandleScreenPositions() {
2049        if (isSelectionHandleShowing()) {
2050            mSelectionHandleController.setStartHandlePosition(
2051                    mStartHandlePoint.getXPix(), mStartHandlePoint.getYPix());
2052            mSelectionHandleController.setEndHandlePosition(
2053                    mEndHandlePoint.getXPix(), mEndHandlePoint.getYPix());
2054        }
2055
2056        if (isInsertionHandleShowing()) {
2057            mInsertionHandleController.setHandlePosition(
2058                    mInsertionHandlePoint.getXPix(), mInsertionHandlePoint.getYPix());
2059        }
2060    }
2061
2062    private void hideHandles() {
2063        if (mSelectionHandleController != null) {
2064            mSelectionHandleController.hideAndDisallowAutomaticShowing();
2065        }
2066        if (mInsertionHandleController != null) {
2067            mInsertionHandleController.hideAndDisallowAutomaticShowing();
2068        }
2069        mPositionObserver.removeListener(mPositionListener);
2070    }
2071
2072    private void showSelectActionBar() {
2073        if (mActionMode != null) {
2074            mActionMode.invalidate();
2075            return;
2076        }
2077
2078        // Start a new action mode with a SelectActionModeCallback.
2079        SelectActionModeCallback.ActionHandler actionHandler =
2080                new SelectActionModeCallback.ActionHandler() {
2081            @Override
2082            public void selectAll() {
2083                mImeAdapter.selectAll();
2084            }
2085
2086            @Override
2087            public void cut() {
2088                mImeAdapter.cut();
2089            }
2090
2091            @Override
2092            public void copy() {
2093                mImeAdapter.copy();
2094            }
2095
2096            @Override
2097            public void paste() {
2098                mImeAdapter.paste();
2099            }
2100
2101            @Override
2102            public void share() {
2103                final String query = getSelectedText();
2104                if (TextUtils.isEmpty(query)) return;
2105
2106                Intent send = new Intent(Intent.ACTION_SEND);
2107                send.setType("text/plain");
2108                send.putExtra(Intent.EXTRA_TEXT, query);
2109                try {
2110                    Intent i = Intent.createChooser(send, getContext().getString(
2111                            R.string.actionbar_share));
2112                    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2113                    getContext().startActivity(i);
2114                } catch (android.content.ActivityNotFoundException ex) {
2115                    // If no app handles it, do nothing.
2116                }
2117            }
2118
2119            @Override
2120            public void search() {
2121                final String query = getSelectedText();
2122                if (TextUtils.isEmpty(query)) return;
2123
2124                // See if ContentViewClient wants to override
2125                if (getContentViewClient().doesPerformWebSearch()) {
2126                    getContentViewClient().performWebSearch(query);
2127                    return;
2128                }
2129
2130                Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
2131                i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2132                i.putExtra(SearchManager.QUERY, query);
2133                i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
2134                if (!(getContext() instanceof Activity)) {
2135                    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2136                }
2137                try {
2138                    getContext().startActivity(i);
2139                } catch (android.content.ActivityNotFoundException ex) {
2140                    // If no app handles it, do nothing.
2141                }
2142            }
2143
2144            @Override
2145            public boolean isSelectionEditable() {
2146                return mSelectionEditable;
2147            }
2148
2149            @Override
2150            public void onDestroyActionMode() {
2151                mActionMode = null;
2152                if (mUnselectAllOnActionModeDismiss) mImeAdapter.unselect();
2153                getContentViewClient().onContextualActionBarHidden();
2154            }
2155
2156            @Override
2157            public boolean isShareAvailable() {
2158                Intent intent = new Intent(Intent.ACTION_SEND);
2159                intent.setType("text/plain");
2160                return getContext().getPackageManager().queryIntentActivities(intent,
2161                        PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2162            }
2163
2164            @Override
2165            public boolean isWebSearchAvailable() {
2166                if (getContentViewClient().doesPerformWebSearch()) return true;
2167                Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
2168                intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2169                return getContext().getPackageManager().queryIntentActivities(intent,
2170                        PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2171            }
2172        };
2173        mActionMode = null;
2174        // On ICS, startActionMode throws an NPE when getParent() is null.
2175        if (mContainerView.getParent() != null) {
2176            mActionMode = mContainerView.startActionMode(
2177                    getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler,
2178                            nativeIsIncognito(mNativeContentViewCore)));
2179        }
2180        mUnselectAllOnActionModeDismiss = true;
2181        if (mActionMode == null) {
2182            // There is no ActionMode, so remove the selection.
2183            mImeAdapter.unselect();
2184        } else {
2185            getContentViewClient().onContextualActionBarShown();
2186        }
2187    }
2188
2189    public boolean getUseDesktopUserAgent() {
2190        if (mNativeContentViewCore != 0) {
2191            return nativeGetUseDesktopUserAgent(mNativeContentViewCore);
2192        }
2193        return false;
2194    }
2195
2196    /**
2197     * Set whether or not we're using a desktop user agent for the currently loaded page.
2198     * @param override If true, use a desktop user agent.  Use a mobile one otherwise.
2199     * @param reloadOnChange Reload the page if the UA has changed.
2200     */
2201    public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange) {
2202        if (mNativeContentViewCore != 0) {
2203            nativeSetUseDesktopUserAgent(mNativeContentViewCore, override, reloadOnChange);
2204        }
2205    }
2206
2207    public void clearSslPreferences() {
2208        nativeClearSslPreferences(mNativeContentViewCore);
2209    }
2210
2211    private boolean isSelectionHandleShowing() {
2212        return mSelectionHandleController != null && mSelectionHandleController.isShowing();
2213    }
2214
2215    private boolean isInsertionHandleShowing() {
2216        return mInsertionHandleController != null && mInsertionHandleController.isShowing();
2217    }
2218
2219    // Makes the insertion/selection handles invisible. They will fade back in shortly after the
2220    // last call to scheduleTextHandleFadeIn (or temporarilyHideTextHandles).
2221    private void temporarilyHideTextHandles() {
2222        if (isSelectionHandleShowing() && !mSelectionHandleController.isDragging()) {
2223            mSelectionHandleController.setHandleVisibility(HandleView.INVISIBLE);
2224        }
2225        if (isInsertionHandleShowing() && !mInsertionHandleController.isDragging()) {
2226            mInsertionHandleController.setHandleVisibility(HandleView.INVISIBLE);
2227        }
2228        scheduleTextHandleFadeIn();
2229    }
2230
2231    private boolean allowTextHandleFadeIn() {
2232        if (mTouchScrollInProgress) return false;
2233
2234        if (mPopupZoomer.isShowing()) return false;
2235
2236        return true;
2237    }
2238
2239    // Cancels any pending fade in and schedules a new one.
2240    private void scheduleTextHandleFadeIn() {
2241        if (!isInsertionHandleShowing() && !isSelectionHandleShowing()) return;
2242
2243        if (mDeferredHandleFadeInRunnable == null) {
2244            mDeferredHandleFadeInRunnable = new Runnable() {
2245                @Override
2246                public void run() {
2247                    if (!allowTextHandleFadeIn()) {
2248                        // Delay fade in until it is allowed.
2249                        scheduleTextHandleFadeIn();
2250                    } else {
2251                        if (isSelectionHandleShowing()) {
2252                            mSelectionHandleController.beginHandleFadeIn();
2253                        }
2254                        if (isInsertionHandleShowing()) {
2255                            mInsertionHandleController.beginHandleFadeIn();
2256                        }
2257                    }
2258                }
2259            };
2260        }
2261
2262        mContainerView.removeCallbacks(mDeferredHandleFadeInRunnable);
2263        mContainerView.postDelayed(mDeferredHandleFadeInRunnable, TEXT_HANDLE_FADE_IN_DELAY);
2264    }
2265
2266    /**
2267     * Shows the IME if the focused widget could accept text input.
2268     */
2269    public void showImeIfNeeded() {
2270        if (mNativeContentViewCore != 0) nativeShowImeIfNeeded(mNativeContentViewCore);
2271    }
2272
2273    /**
2274     * Hides the IME if the containerView is the active view for IME.
2275     */
2276    public void hideImeIfNeeded() {
2277        // Hide input method window from the current view synchronously
2278        // because ImeAdapter does so asynchronouly with a delay, and
2279        // by the time when ImeAdapter dismisses the input, the
2280        // containerView may have lost focus.
2281        // We cannot trust ContentViewClient#onImeStateChangeRequested to
2282        // hide the input window because it has an empty default implementation.
2283        // So we need to explicitly hide the input method window here.
2284        if (mInputMethodManagerWrapper.isActive(mContainerView)) {
2285            mInputMethodManagerWrapper.hideSoftInputFromWindow(
2286                    mContainerView.getWindowToken(), 0, null);
2287        }
2288        getContentViewClient().onImeStateChangeRequested(false);
2289    }
2290
2291    @SuppressWarnings("unused")
2292    @CalledByNative
2293    private void updateFrameInfo(
2294            float scrollOffsetX, float scrollOffsetY,
2295            float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
2296            float contentWidth, float contentHeight,
2297            float viewportWidth, float viewportHeight,
2298            float controlsOffsetYCss, float contentOffsetYCss,
2299            float overdrawBottomHeightCss) {
2300        TraceEvent.instant("ContentViewCore:updateFrameInfo");
2301        // Adjust contentWidth/Height to be always at least as big as
2302        // the actual viewport (as set by onSizeChanged).
2303        final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2304        contentWidth = Math.max(contentWidth,
2305                mViewportWidthPix / (deviceScale * pageScaleFactor));
2306        contentHeight = Math.max(contentHeight,
2307                mViewportHeightPix / (deviceScale * pageScaleFactor));
2308        final float contentOffsetYPix = mRenderCoordinates.fromDipToPix(contentOffsetYCss);
2309
2310        final boolean contentSizeChanged =
2311                contentWidth != mRenderCoordinates.getContentWidthCss()
2312                || contentHeight != mRenderCoordinates.getContentHeightCss();
2313        final boolean scaleLimitsChanged =
2314                minPageScaleFactor != mRenderCoordinates.getMinPageScaleFactor()
2315                || maxPageScaleFactor != mRenderCoordinates.getMaxPageScaleFactor();
2316        final boolean pageScaleChanged =
2317                pageScaleFactor != mRenderCoordinates.getPageScaleFactor();
2318        final boolean scrollChanged =
2319                pageScaleChanged
2320                || scrollOffsetX != mRenderCoordinates.getScrollX()
2321                || scrollOffsetY != mRenderCoordinates.getScrollY();
2322        final boolean contentOffsetChanged =
2323                contentOffsetYPix != mRenderCoordinates.getContentOffsetYPix();
2324
2325        final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
2326        final boolean needUpdateZoomControls = scaleLimitsChanged || scrollChanged;
2327        final boolean needTemporarilyHideHandles = scrollChanged;
2328
2329        if (needHidePopupZoomer) mPopupZoomer.hide(true);
2330
2331        if (scrollChanged) {
2332            mContainerViewInternals.onScrollChanged(
2333                    (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetX),
2334                    (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetY),
2335                    (int) mRenderCoordinates.getScrollXPix(),
2336                    (int) mRenderCoordinates.getScrollYPix());
2337        }
2338
2339        mRenderCoordinates.updateFrameInfo(
2340                scrollOffsetX, scrollOffsetY,
2341                contentWidth, contentHeight,
2342                viewportWidth, viewportHeight,
2343                pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
2344                contentOffsetYPix);
2345        onRenderCoordinatesUpdated();
2346
2347        if (scrollChanged || contentOffsetChanged) {
2348            for (mGestureStateListenersIterator.rewind();
2349                    mGestureStateListenersIterator.hasNext();) {
2350                mGestureStateListenersIterator.next().onScrollOffsetOrExtentChanged(
2351                        computeVerticalScrollOffset(),
2352                        computeVerticalScrollExtent());
2353            }
2354        }
2355
2356        if (needTemporarilyHideHandles) temporarilyHideTextHandles();
2357        if (needUpdateZoomControls) mZoomControlsDelegate.updateZoomControls();
2358        if (contentOffsetChanged) updateHandleScreenPositions();
2359
2360        // Update offsets for fullscreen.
2361        final float controlsOffsetPix = controlsOffsetYCss * deviceScale;
2362        final float overdrawBottomHeightPix = overdrawBottomHeightCss * deviceScale;
2363        getContentViewClient().onOffsetsForFullscreenChanged(
2364                controlsOffsetPix, contentOffsetYPix, overdrawBottomHeightPix);
2365
2366        mPendingRendererFrame = true;
2367        if (mBrowserAccessibilityManager != null) {
2368            mBrowserAccessibilityManager.notifyFrameInfoInitialized();
2369        }
2370    }
2371
2372    @CalledByNative
2373    private void updateImeAdapter(long nativeImeAdapterAndroid, int textInputType,
2374            String text, int selectionStart, int selectionEnd,
2375            int compositionStart, int compositionEnd, boolean showImeIfNeeded,
2376            boolean isNonImeChange) {
2377        TraceEvent.begin();
2378        mSelectionEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
2379
2380        if (mActionMode != null) mActionMode.invalidate();
2381
2382        mImeAdapter.attachAndShowIfNeeded(nativeImeAdapterAndroid, textInputType, showImeIfNeeded);
2383
2384        if (mInputConnection != null) {
2385            mInputConnection.updateState(text, selectionStart, selectionEnd, compositionStart,
2386                    compositionEnd, isNonImeChange);
2387        }
2388        TraceEvent.end();
2389    }
2390
2391    @SuppressWarnings("unused")
2392    @CalledByNative
2393    private void setTitle(String title) {
2394        getContentViewClient().onUpdateTitle(title);
2395    }
2396
2397    /**
2398     * Called (from native) when the <select> popup needs to be shown.
2399     * @param items           Items to show.
2400     * @param enabled         POPUP_ITEM_TYPEs for items.
2401     * @param multiple        Whether the popup menu should support multi-select.
2402     * @param selectedIndices Indices of selected items.
2403     */
2404    @SuppressWarnings("unused")
2405    @CalledByNative
2406    private void showSelectPopup(String[] items, int[] enabled, boolean multiple,
2407            int[] selectedIndices) {
2408        if (mContainerView.getParent() == null || mContainerView.getVisibility() != View.VISIBLE) {
2409            selectPopupMenuItems(null);
2410            return;
2411        }
2412
2413        hideSelectPopup();
2414        assert items.length == enabled.length;
2415        List<SelectPopupItem> popupItems = new ArrayList<SelectPopupItem>();
2416        for (int i = 0; i < items.length; i++) {
2417            popupItems.add(new SelectPopupItem(items[i], enabled[i]));
2418        }
2419        mSelectPopupDialog = SelectPopupDialog.show(this, popupItems, multiple, selectedIndices);
2420    }
2421
2422    /**
2423     * Called when the <select> popup needs to be hidden.
2424     */
2425    @CalledByNative
2426    private void hideSelectPopup() {
2427        if (mSelectPopupDialog != null) {
2428            mSelectPopupDialog.hide();
2429            mSelectPopupDialog = null;
2430        }
2431    }
2432
2433    /**
2434     * @return The visible select popup dialog being shown.
2435     */
2436    public SelectPopupDialog getSelectPopupForTest() {
2437        return mSelectPopupDialog;
2438    }
2439
2440    @SuppressWarnings("unused")
2441    @CalledByNative
2442    private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
2443        mPopupZoomer.setBitmap(zoomedBitmap);
2444        mPopupZoomer.show(targetRect);
2445        temporarilyHideTextHandles();
2446    }
2447
2448    @SuppressWarnings("unused")
2449    @CalledByNative
2450    private TouchEventSynthesizer createTouchEventSynthesizer() {
2451        return new TouchEventSynthesizer(this);
2452    }
2453
2454    @SuppressWarnings("unused")
2455    @CalledByNative
2456    private void onSelectionChanged(String text) {
2457        mLastSelectedText = text;
2458        getContentViewClient().onSelectionChanged(text);
2459    }
2460
2461    @SuppressWarnings("unused")
2462    @CalledByNative
2463    private void onSelectionBoundsChanged(Rect anchorRectDip, int anchorDir, Rect focusRectDip,
2464            int focusDir, boolean isAnchorFirst) {
2465        // All coordinates are in DIP.
2466        int x1 = anchorRectDip.left;
2467        int y1 = anchorRectDip.bottom;
2468        int x2 = focusRectDip.left;
2469        int y2 = focusRectDip.bottom;
2470
2471        if (x1 != x2 || y1 != y2 ||
2472                (mSelectionHandleController != null && mSelectionHandleController.isDragging())) {
2473            if (mInsertionHandleController != null) {
2474                mInsertionHandleController.hide();
2475            }
2476            if (isAnchorFirst) {
2477                mStartHandlePoint.setLocalDip(x1, y1);
2478                mEndHandlePoint.setLocalDip(x2, y2);
2479            } else {
2480                mStartHandlePoint.setLocalDip(x2, y2);
2481                mEndHandlePoint.setLocalDip(x1, y1);
2482            }
2483
2484            boolean wereSelectionHandlesShowing = getSelectionHandleController().isShowing();
2485
2486            getSelectionHandleController().onSelectionChanged(anchorDir, focusDir);
2487            updateHandleScreenPositions();
2488            mHasSelection = true;
2489
2490            if (!wereSelectionHandlesShowing && getSelectionHandleController().isShowing()) {
2491                // TODO(cjhopman): Remove this when there is a better signal that long press caused
2492                // a selection. See http://crbug.com/150151.
2493                mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2494            }
2495
2496        } else {
2497            mUnselectAllOnActionModeDismiss = false;
2498            hideSelectActionBar();
2499            if (x1 != 0 && y1 != 0 && mSelectionEditable) {
2500                // Selection is a caret, and a text field is focused.
2501                if (mSelectionHandleController != null) {
2502                    mSelectionHandleController.hide();
2503                }
2504                mInsertionHandlePoint.setLocalDip(x1, y1);
2505
2506                getInsertionHandleController().onCursorPositionChanged();
2507                updateHandleScreenPositions();
2508                if (mInputMethodManagerWrapper.isWatchingCursor(mContainerView)) {
2509                    final int xPix = (int) mInsertionHandlePoint.getXPix();
2510                    final int yPix = (int) mInsertionHandlePoint.getYPix();
2511                    mInputMethodManagerWrapper.updateCursor(
2512                            mContainerView, xPix, yPix, xPix, yPix);
2513                }
2514            } else {
2515                // Deselection
2516                if (mSelectionHandleController != null) {
2517                    mSelectionHandleController.hideAndDisallowAutomaticShowing();
2518                }
2519                if (mInsertionHandleController != null) {
2520                    mInsertionHandleController.hideAndDisallowAutomaticShowing();
2521                }
2522            }
2523            mHasSelection = false;
2524        }
2525        if (isSelectionHandleShowing() || isInsertionHandleShowing()) {
2526            mPositionObserver.addListener(mPositionListener);
2527        }
2528    }
2529
2530    @CalledByNative
2531    private void setSelectionRootBounds(Rect bounds) {
2532        mTopLeftVisibilityClippingPoint.setLocalDip(bounds.left, bounds.top);
2533        mBottomRightVisibilityClippingPoint.setLocalDip(bounds.right, bounds.bottom);
2534        updateInsertionSelectionVisibleBounds();
2535    }
2536
2537    private void updateInsertionSelectionVisibleBounds() {
2538        if (mSelectionHandleController == null && mInsertionHandleController == null) {
2539            return;
2540        }
2541
2542        int x1 = Math.round(mTopLeftVisibilityClippingPoint.getXPix());
2543        int y1 = Math.round(mTopLeftVisibilityClippingPoint.getYPix());
2544        int x2 = Math.round(mBottomRightVisibilityClippingPoint.getXPix());
2545        int y2 = Math.round(mBottomRightVisibilityClippingPoint.getYPix());
2546
2547        if (mSelectionHandleController != null) {
2548            mSelectionHandleController.setVisibleClippingRectangle(x1, y1, x2, y2);
2549        }
2550
2551        if (mInsertionHandleController != null) {
2552            mInsertionHandleController.setVisibleClippingRectangle(x1, y1, x2, y2);
2553        }
2554    }
2555
2556    @SuppressWarnings("unused")
2557    @CalledByNative
2558    private static void onEvaluateJavaScriptResult(
2559            String jsonResult, JavaScriptCallback callback) {
2560        callback.handleJavaScriptResult(jsonResult);
2561    }
2562
2563    @SuppressWarnings("unused")
2564    @CalledByNative
2565    private void showPastePopup(int xDip, int yDip) {
2566        mInsertionHandlePoint.setLocalDip(xDip, yDip);
2567        getInsertionHandleController().showHandle();
2568        updateHandleScreenPositions();
2569        getInsertionHandleController().showHandleWithPastePopup();
2570    }
2571
2572    @SuppressWarnings("unused")
2573    @CalledByNative
2574    private void onRenderProcessSwap(int oldPid, int newPid) {
2575        if (!mInForeground) {
2576            ChildProcessLauncher.getBindingManager().setInForeground(newPid, false);
2577        } else if (oldPid != newPid) {
2578            ChildProcessLauncher.getBindingManager().setInForeground(oldPid, false);
2579            ChildProcessLauncher.getBindingManager().setInForeground(newPid, true);
2580        }
2581
2582        attachImeAdapter();
2583    }
2584
2585    @SuppressWarnings("unused")
2586    @CalledByNative
2587    private void onWebContentsConnected() {
2588        attachImeAdapter();
2589    }
2590
2591    /**
2592     * Attaches the native ImeAdapter object to the java ImeAdapter to allow communication via JNI.
2593     */
2594    public void attachImeAdapter() {
2595        if (mImeAdapter != null && mNativeContentViewCore != 0) {
2596            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2597        }
2598    }
2599
2600    /**
2601     * @see View#hasFocus()
2602     */
2603    @CalledByNative
2604    public boolean hasFocus() {
2605        return mContainerView.hasFocus();
2606    }
2607
2608    /**
2609     * Checks whether the ContentViewCore can be zoomed in.
2610     *
2611     * @return True if the ContentViewCore can be zoomed in.
2612     */
2613    // This method uses the term 'zoom' for legacy reasons, but relates
2614    // to what chrome calls the 'page scale factor'.
2615    public boolean canZoomIn() {
2616        final float zoomInExtent = mRenderCoordinates.getMaxPageScaleFactor()
2617                - mRenderCoordinates.getPageScaleFactor();
2618        return zoomInExtent > ZOOM_CONTROLS_EPSILON;
2619    }
2620
2621    /**
2622     * Checks whether the ContentViewCore can be zoomed out.
2623     *
2624     * @return True if the ContentViewCore can be zoomed out.
2625     */
2626    // This method uses the term 'zoom' for legacy reasons, but relates
2627    // to what chrome calls the 'page scale factor'.
2628    public boolean canZoomOut() {
2629        final float zoomOutExtent = mRenderCoordinates.getPageScaleFactor()
2630                - mRenderCoordinates.getMinPageScaleFactor();
2631        return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
2632    }
2633
2634    /**
2635     * Zooms in the ContentViewCore by 25% (or less if that would result in
2636     * zooming in more than possible).
2637     *
2638     * @return True if there was a zoom change, false otherwise.
2639     */
2640    // This method uses the term 'zoom' for legacy reasons, but relates
2641    // to what chrome calls the 'page scale factor'.
2642    public boolean zoomIn() {
2643        if (!canZoomIn()) {
2644            return false;
2645        }
2646        return pinchByDelta(1.25f);
2647    }
2648
2649    /**
2650     * Zooms out the ContentViewCore by 20% (or less if that would result in
2651     * zooming out more than possible).
2652     *
2653     * @return True if there was a zoom change, false otherwise.
2654     */
2655    // This method uses the term 'zoom' for legacy reasons, but relates
2656    // to what chrome calls the 'page scale factor'.
2657    public boolean zoomOut() {
2658        if (!canZoomOut()) {
2659            return false;
2660        }
2661        return pinchByDelta(0.8f);
2662    }
2663
2664    /**
2665     * Resets the zoom factor of the ContentViewCore.
2666     *
2667     * @return True if there was a zoom change, false otherwise.
2668     */
2669    // This method uses the term 'zoom' for legacy reasons, but relates
2670    // to what chrome calls the 'page scale factor'.
2671    public boolean zoomReset() {
2672        // The page scale factor is initialized to mNativeMinimumScale when
2673        // the page finishes loading. Thus sets it back to mNativeMinimumScale.
2674        if (!canZoomOut()) return false;
2675        return pinchByDelta(
2676                mRenderCoordinates.getMinPageScaleFactor()
2677                        / mRenderCoordinates.getPageScaleFactor());
2678    }
2679
2680    /**
2681     * Simulate a pinch zoom gesture.
2682     *
2683     * @param delta the factor by which the current page scale should be multiplied by.
2684     * @return whether the gesture was sent.
2685     */
2686    public boolean pinchByDelta(float delta) {
2687        if (mNativeContentViewCore == 0) return false;
2688
2689        long timeMs = SystemClock.uptimeMillis();
2690        int xPix = getViewportWidthPix() / 2;
2691        int yPix = getViewportHeightPix() / 2;
2692
2693        nativePinchBegin(mNativeContentViewCore, timeMs, xPix, yPix);
2694        nativePinchBy(mNativeContentViewCore, timeMs, xPix, yPix, delta);
2695        nativePinchEnd(mNativeContentViewCore, timeMs);
2696
2697        return true;
2698    }
2699
2700    /**
2701     * Invokes the graphical zoom picker widget for this ContentView.
2702     */
2703    public void invokeZoomPicker() {
2704        mZoomControlsDelegate.invokeZoomPicker();
2705    }
2706
2707    /**
2708     * Enables or disables inspection of JavaScript objects added via
2709     * {@link #addJavascriptInterface(Object, String)} by means of Object.keys() method and
2710     * &quot;for .. in&quot; loop. Being able to inspect JavaScript objects is useful
2711     * when debugging hybrid Android apps, but can't be enabled for legacy applications due
2712     * to compatibility risks.
2713     *
2714     * @param allow Whether to allow JavaScript objects inspection.
2715     */
2716    public void setAllowJavascriptInterfacesInspection(boolean allow) {
2717        nativeSetAllowJavascriptInterfacesInspection(mNativeContentViewCore, allow);
2718    }
2719
2720    /**
2721     * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
2722     * and automatically pass in {@link JavascriptInterface} as the required annotation.
2723     *
2724     * @param object The Java object to inject into the ContentViewCore's JavaScript context.  Null
2725     *               values are ignored.
2726     * @param name   The name used to expose the instance in JavaScript.
2727     */
2728    public void addJavascriptInterface(Object object, String name) {
2729        addPossiblyUnsafeJavascriptInterface(object, name, JavascriptInterface.class);
2730    }
2731
2732    /**
2733     * This method injects the supplied Java object into the ContentViewCore.
2734     * The object is injected into the JavaScript context of the main frame,
2735     * using the supplied name. This allows the Java object to be accessed from
2736     * JavaScript. Note that that injected objects will not appear in
2737     * JavaScript until the page is next (re)loaded. For example:
2738     * <pre> view.addJavascriptInterface(new Object(), "injectedObject");
2739     * view.loadData("<!DOCTYPE html><title></title>", "text/html", null);
2740     * view.loadUrl("javascript:alert(injectedObject.toString())");</pre>
2741     * <p><strong>IMPORTANT:</strong>
2742     * <ul>
2743     * <li> addJavascriptInterface() can be used to allow JavaScript to control
2744     * the host application. This is a powerful feature, but also presents a
2745     * security risk. Use of this method in a ContentViewCore containing
2746     * untrusted content could allow an attacker to manipulate the host
2747     * application in unintended ways, executing Java code with the permissions
2748     * of the host application. Use extreme care when using this method in a
2749     * ContentViewCore which could contain untrusted content. Particular care
2750     * should be taken to avoid unintentional access to inherited methods, such
2751     * as {@link Object#getClass()}. To prevent access to inherited methods,
2752     * pass an annotation for {@code requiredAnnotation}.  This will ensure
2753     * that only methods with {@code requiredAnnotation} are exposed to the
2754     * Javascript layer.  {@code requiredAnnotation} will be passed to all
2755     * subsequently injected Java objects if any methods return an object.  This
2756     * means the same restrictions (or lack thereof) will apply.  Alternatively,
2757     * {@link #addJavascriptInterface(Object, String)} can be called, which
2758     * automatically uses the {@link JavascriptInterface} annotation.
2759     * <li> JavaScript interacts with Java objects on a private, background
2760     * thread of the ContentViewCore. Care is therefore required to maintain
2761     * thread safety.</li>
2762     * </ul></p>
2763     *
2764     * @param object             The Java object to inject into the
2765     *                           ContentViewCore's JavaScript context. Null
2766     *                           values are ignored.
2767     * @param name               The name used to expose the instance in
2768     *                           JavaScript.
2769     * @param requiredAnnotation Restrict exposed methods to ones with this
2770     *                           annotation.  If {@code null} all methods are
2771     *                           exposed.
2772     *
2773     */
2774    public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
2775            Class<? extends Annotation> requiredAnnotation) {
2776        if (mNativeContentViewCore != 0 && object != null) {
2777            mJavaScriptInterfaces.put(name, object);
2778            nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation,
2779                    mRetainedJavaScriptObjects);
2780        }
2781    }
2782
2783    /**
2784     * Removes a previously added JavaScript interface with the given name.
2785     *
2786     * @param name The name of the interface to remove.
2787     */
2788    public void removeJavascriptInterface(String name) {
2789        mJavaScriptInterfaces.remove(name);
2790        if (mNativeContentViewCore != 0) {
2791            nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2792        }
2793    }
2794
2795    /**
2796     * Return the current scale of the ContentView.
2797     * @return The current page scale factor.
2798     */
2799    public float getScale() {
2800        return mRenderCoordinates.getPageScaleFactor();
2801    }
2802
2803    /**
2804     * If the view is ready to draw contents to the screen. In hardware mode,
2805     * the initialization of the surface texture may not occur until after the
2806     * view has been added to the layout. This method will return {@code true}
2807     * once the texture is actually ready.
2808     */
2809    public boolean isReady() {
2810        if (mNativeContentViewCore == 0) return false;
2811        return nativeIsRenderWidgetHostViewReady(mNativeContentViewCore);
2812    }
2813
2814    @CalledByNative
2815    private void startContentIntent(String contentUrl) {
2816        getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2817    }
2818
2819    @Override
2820    public void onAccessibilityStateChanged(boolean enabled) {
2821        setAccessibilityState(enabled);
2822    }
2823
2824    /**
2825     * Determines whether or not this ContentViewCore can handle this accessibility action.
2826     * @param action The action to perform.
2827     * @return Whether or not this action is supported.
2828     */
2829    public boolean supportsAccessibilityAction(int action) {
2830        return mAccessibilityInjector.supportsAccessibilityAction(action);
2831    }
2832
2833    /**
2834     * Attempts to perform an accessibility action on the web content.  If the accessibility action
2835     * cannot be processed, it returns {@code null}, allowing the caller to know to call the
2836     * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value.
2837     * Otherwise the return value from this method should be used.
2838     * @param action The action to perform.
2839     * @param arguments Optional action arguments.
2840     * @return Whether the action was performed or {@code null} if the call should be delegated to
2841     *         the super {@link View} class.
2842     */
2843    public boolean performAccessibilityAction(int action, Bundle arguments) {
2844        if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2845            return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2846        }
2847
2848        return false;
2849    }
2850
2851    /**
2852     * Set the BrowserAccessibilityManager, used for native accessibility
2853     * (not script injection). This is only set when system accessibility
2854     * has been enabled.
2855     * @param manager The new BrowserAccessibilityManager.
2856     */
2857    public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
2858        mBrowserAccessibilityManager = manager;
2859    }
2860
2861    /**
2862     * Get the BrowserAccessibilityManager, used for native accessibility
2863     * (not script injection). This will return null when system accessibility
2864     * is not enabled.
2865     * @return This view's BrowserAccessibilityManager.
2866     */
2867    public BrowserAccessibilityManager getBrowserAccessibilityManager() {
2868        return mBrowserAccessibilityManager;
2869    }
2870
2871    /**
2872     * If native accessibility (not script injection) is enabled, and if this is
2873     * running on JellyBean or later, returns an AccessibilityNodeProvider that
2874     * implements native accessibility for this view. Returns null otherwise.
2875     * Lazily initializes native accessibility here if it's allowed.
2876     * @return The AccessibilityNodeProvider, if available, or null otherwise.
2877     */
2878    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
2879        if (mBrowserAccessibilityManager != null) {
2880            return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
2881        }
2882
2883        if (mNativeAccessibilityAllowed &&
2884                !mNativeAccessibilityEnabled &&
2885                mNativeContentViewCore != 0 &&
2886                Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
2887            mNativeAccessibilityEnabled = true;
2888            nativeSetAccessibilityEnabled(mNativeContentViewCore, true);
2889        }
2890
2891        return null;
2892    }
2893
2894    /**
2895     * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
2896     */
2897    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2898        // Note: this is only used by the script-injecting accessibility code.
2899        mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
2900    }
2901
2902    /**
2903     * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
2904     */
2905    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
2906        // Note: this is only used by the script-injecting accessibility code.
2907        event.setClassName(this.getClass().getName());
2908
2909        // Identify where the top-left of the screen currently points to.
2910        event.setScrollX(mRenderCoordinates.getScrollXPixInt());
2911        event.setScrollY(mRenderCoordinates.getScrollYPixInt());
2912
2913        // The maximum scroll values are determined by taking the content dimensions and
2914        // subtracting off the actual dimensions of the ChromeView.
2915        int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt());
2916        int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt());
2917        event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0);
2918
2919        // Setting the maximum scroll values requires API level 15 or higher.
2920        final int SDK_VERSION_REQUIRED_TO_SET_SCROLL = 15;
2921        if (Build.VERSION.SDK_INT >= SDK_VERSION_REQUIRED_TO_SET_SCROLL) {
2922            event.setMaxScrollX(maxScrollXPix);
2923            event.setMaxScrollY(maxScrollYPix);
2924        }
2925    }
2926
2927    /**
2928     * Returns whether accessibility script injection is enabled on the device
2929     */
2930    public boolean isDeviceAccessibilityScriptInjectionEnabled() {
2931        try {
2932            if (!CommandLine.getInstance().hasSwitch(
2933                    ContentSwitches.ENABLE_ACCESSIBILITY_SCRIPT_INJECTION)) {
2934                return false;
2935            }
2936
2937            if (!mContentSettings.getJavaScriptEnabled()) {
2938                return false;
2939            }
2940
2941            int result = getContext().checkCallingOrSelfPermission(
2942                    android.Manifest.permission.INTERNET);
2943            if (result != PackageManager.PERMISSION_GRANTED) {
2944                return false;
2945            }
2946
2947            Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
2948            field.setAccessible(true);
2949            String accessibilityScriptInjection = (String) field.get(null);
2950            ContentResolver contentResolver = getContext().getContentResolver();
2951
2952            if (mAccessibilityScriptInjectionObserver == null) {
2953                ContentObserver contentObserver = new ContentObserver(new Handler()) {
2954                    @Override
2955                    public void onChange(boolean selfChange, Uri uri) {
2956                        setAccessibilityState(mAccessibilityManager.isEnabled());
2957                    }
2958                };
2959                contentResolver.registerContentObserver(
2960                    Settings.Secure.getUriFor(accessibilityScriptInjection),
2961                    false,
2962                    contentObserver);
2963                mAccessibilityScriptInjectionObserver = contentObserver;
2964            }
2965
2966            return Settings.Secure.getInt(contentResolver, accessibilityScriptInjection, 0) == 1;
2967        } catch (NoSuchFieldException e) {
2968            // Do nothing, default to false.
2969        } catch (IllegalAccessException e) {
2970            // Do nothing, default to false.
2971        }
2972        return false;
2973    }
2974
2975    /**
2976     * Returns whether or not accessibility injection is being used.
2977     */
2978    public boolean isInjectingAccessibilityScript() {
2979        return mAccessibilityInjector.accessibilityIsAvailable();
2980    }
2981
2982    /**
2983     * Turns browser accessibility on or off.
2984     * If |state| is |false|, this turns off both native and injected accessibility.
2985     * Otherwise, if accessibility script injection is enabled, this will enable the injected
2986     * accessibility scripts. Native accessibility is enabled on demand.
2987     */
2988    public void setAccessibilityState(boolean state) {
2989        if (!state) {
2990            setInjectedAccessibility(false);
2991            mNativeAccessibilityAllowed = false;
2992            mTouchExplorationEnabled = false;
2993        } else {
2994            boolean useScriptInjection = isDeviceAccessibilityScriptInjectionEnabled();
2995            setInjectedAccessibility(useScriptInjection);
2996            mNativeAccessibilityAllowed = !useScriptInjection;
2997            mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
2998        }
2999    }
3000
3001    /**
3002     * Enable or disable injected accessibility features
3003     */
3004    public void setInjectedAccessibility(boolean enabled) {
3005        mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
3006        mAccessibilityInjector.setScriptEnabled(enabled);
3007    }
3008
3009    /**
3010     * Stop any TTS notifications that are currently going on.
3011     */
3012    public void stopCurrentAccessibilityNotifications() {
3013        mAccessibilityInjector.onPageLostFocus();
3014    }
3015
3016    /**
3017     * Inform WebKit that Fullscreen mode has been exited by the user.
3018     */
3019    public void exitFullscreen() {
3020        if (mNativeContentViewCore != 0) nativeExitFullscreen(mNativeContentViewCore);
3021    }
3022
3023    /**
3024     * Changes whether hiding the top controls is enabled.
3025     *
3026     * @param enableHiding Whether hiding the top controls should be enabled or not.
3027     * @param enableShowing Whether showing the top controls should be enabled or not.
3028     * @param animate Whether the transition should be animated or not.
3029     */
3030    public void updateTopControlsState(boolean enableHiding, boolean enableShowing,
3031            boolean animate) {
3032        if (mNativeContentViewCore != 0) {
3033            nativeUpdateTopControlsState(
3034                    mNativeContentViewCore, enableHiding, enableShowing, animate);
3035        }
3036    }
3037
3038    /**
3039     * Callback factory method for nativeGetNavigationHistory().
3040     */
3041    @CalledByNative
3042    private void addToNavigationHistory(Object history, int index, String url, String virtualUrl,
3043            String originalUrl, String title, Bitmap favicon) {
3044        NavigationEntry entry = new NavigationEntry(
3045                index, url, virtualUrl, originalUrl, title, favicon);
3046        ((NavigationHistory) history).addEntry(entry);
3047    }
3048
3049    /**
3050     * Get a copy of the navigation history of the view.
3051     */
3052    public NavigationHistory getNavigationHistory() {
3053        NavigationHistory history = new NavigationHistory();
3054        if (mNativeContentViewCore != 0) {
3055            int currentIndex = nativeGetNavigationHistory(mNativeContentViewCore, history);
3056            history.setCurrentEntryIndex(currentIndex);
3057        }
3058        return history;
3059    }
3060
3061    @Override
3062    public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
3063        NavigationHistory history = new NavigationHistory();
3064        if (mNativeContentViewCore != 0) {
3065            nativeGetDirectedNavigationHistory(
3066                mNativeContentViewCore, history, isForward, itemLimit);
3067        }
3068        return history;
3069    }
3070
3071    /**
3072     * @return The original request URL for the current navigation entry, or null if there is no
3073     *         current entry.
3074     */
3075    public String getOriginalUrlForActiveNavigationEntry() {
3076        if (mNativeContentViewCore != 0) {
3077            return nativeGetOriginalUrlForActiveNavigationEntry(mNativeContentViewCore);
3078        }
3079        return "";
3080    }
3081
3082    /**
3083     * @return The cached copy of render positions and scales.
3084     */
3085    public RenderCoordinates getRenderCoordinates() {
3086        return mRenderCoordinates;
3087    }
3088
3089    @CalledByNative
3090    private int getLocationInWindowX() {
3091        return mLocationInWindowX;
3092    }
3093
3094    @CalledByNative
3095    private int getLocationInWindowY() {
3096        return mLocationInWindowY;
3097    }
3098
3099    @CalledByNative
3100    private static Rect createRect(int x, int y, int right, int bottom) {
3101        return new Rect(x, y, right, bottom);
3102    }
3103
3104    private boolean onAnimate(long frameTimeMicros) {
3105        if (mNativeContentViewCore == 0) return false;
3106        return nativeOnAnimate(mNativeContentViewCore, frameTimeMicros);
3107    }
3108
3109    private void animateIfNecessary(long frameTimeMicros) {
3110        if (mNeedAnimate) {
3111            mNeedAnimate = onAnimate(frameTimeMicros);
3112            if (!mNeedAnimate) removeVSyncSubscriber();
3113        }
3114    }
3115
3116    public void extractSmartClipData(int x, int y, int width, int height) {
3117        if (mNativeContentViewCore != 0) {
3118            nativeExtractSmartClipData(mNativeContentViewCore, x, y, width, height);
3119        }
3120    }
3121
3122    @CalledByNative
3123    private void onSmartClipDataExtracted(String result) {
3124        if (mSmartClipDataListener != null ) {
3125            mSmartClipDataListener.onSmartClipDataExtracted(result);
3126        }
3127    }
3128
3129    public void setSmartClipDataListener(SmartClipDataListener listener) {
3130        mSmartClipDataListener = listener;
3131    }
3132
3133    /**
3134     * Offer a long press gesture to the embedding View, primarily for WebView compatibility.
3135     *
3136     * @return true if the embedder handled the event.
3137     */
3138    private boolean offerLongPressToEmbedder() {
3139        return mContainerView.performLongClick();
3140    }
3141
3142    /**
3143     * Reset scroll and fling accounting, notifying listeners as appropriate.
3144     * This is useful as a failsafe when the input stream may have been interruped.
3145     */
3146    private void resetScrollInProgress() {
3147        if (!isScrollInProgress()) return;
3148
3149        final boolean touchScrollInProgress = mTouchScrollInProgress;
3150        final int potentiallyActiveFlingCount = mPotentiallyActiveFlingCount;
3151
3152        mTouchScrollInProgress = false;
3153        mPotentiallyActiveFlingCount = 0;
3154
3155        if (touchScrollInProgress) updateGestureStateListener(GestureEventType.SCROLL_END);
3156        if (potentiallyActiveFlingCount > 0) updateGestureStateListener(GestureEventType.FLING_END);
3157    }
3158
3159    private native long nativeInit(long webContentsPtr,
3160            long viewAndroidPtr, long windowAndroidPtr);
3161
3162    @CalledByNative
3163    private ContentVideoViewClient getContentVideoViewClient() {
3164        return getContentViewClient().getContentVideoViewClient();
3165    }
3166
3167    @CalledByNative
3168    private boolean shouldBlockMediaRequest(String url) {
3169        return getContentViewClient().shouldBlockMediaRequest(url);
3170    }
3171
3172    @CalledByNative
3173    private void onNativeFlingStopped() {
3174        // Note that mTouchScrollInProgress should normally be false at this
3175        // point, but we reset it anyway as another failsafe.
3176        mTouchScrollInProgress = false;
3177        if (mPotentiallyActiveFlingCount <= 0) return;
3178        mPotentiallyActiveFlingCount--;
3179        updateGestureStateListener(GestureEventType.FLING_END);
3180    }
3181
3182    @Override
3183    public void onScreenOrientationChanged(int orientation) {
3184        sendOrientationChangeEvent(orientation);
3185    }
3186
3187    private native WebContents nativeGetWebContentsAndroid(long nativeContentViewCoreImpl);
3188
3189    private native void nativeOnJavaContentViewCoreDestroyed(long nativeContentViewCoreImpl);
3190
3191    private native void nativeLoadUrl(
3192            long nativeContentViewCoreImpl,
3193            String url,
3194            int loadUrlType,
3195            int transitionType,
3196            int uaOverrideOption,
3197            String extraHeaders,
3198            byte[] postData,
3199            String baseUrlForDataUrl,
3200            String virtualUrlForDataUrl,
3201            boolean canLoadLocalResources);
3202
3203    private native String nativeGetURL(long nativeContentViewCoreImpl);
3204
3205    private native String nativeGetTitle(long nativeContentViewCoreImpl);
3206
3207    private native void nativeShowInterstitialPage(
3208            long nativeContentViewCoreImpl, String url, long nativeInterstitialPageDelegateAndroid);
3209    private native boolean nativeIsShowingInterstitialPage(long nativeContentViewCoreImpl);
3210
3211    private native boolean nativeIsIncognito(long nativeContentViewCoreImpl);
3212
3213    private native void nativeSetFocus(long nativeContentViewCoreImpl, boolean focused);
3214
3215    private native void nativeSendOrientationChangeEvent(
3216            long nativeContentViewCoreImpl, int orientation);
3217
3218    // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
3219    private native boolean nativeOnTouchEvent(
3220            long nativeContentViewCoreImpl, MotionEvent event,
3221            long timeMs, int action, int pointerCount, int historySize, int actionIndex,
3222            float x0, float y0, float x1, float y1,
3223            int pointerId0, int pointerId1,
3224            float touchMajor0, float touchMajor1);
3225
3226    private native int nativeSendMouseMoveEvent(
3227            long nativeContentViewCoreImpl, long timeMs, float x, float y);
3228
3229    private native int nativeSendMouseWheelEvent(
3230            long nativeContentViewCoreImpl, long timeMs, float x, float y, float verticalAxis);
3231
3232    private native void nativeScrollBegin(
3233            long nativeContentViewCoreImpl, long timeMs, float x, float y, float hintX,
3234            float hintY);
3235
3236    private native void nativeScrollEnd(long nativeContentViewCoreImpl, long timeMs);
3237
3238    private native void nativeScrollBy(
3239            long nativeContentViewCoreImpl, long timeMs, float x, float y,
3240            float deltaX, float deltaY);
3241
3242    private native void nativeFlingStart(
3243            long nativeContentViewCoreImpl, long timeMs, float x, float y, float vx, float vy);
3244
3245    private native void nativeFlingCancel(long nativeContentViewCoreImpl, long timeMs);
3246
3247    private native void nativeSingleTap(
3248            long nativeContentViewCoreImpl, long timeMs, float x, float y);
3249
3250    private native void nativeDoubleTap(
3251            long nativeContentViewCoreImpl, long timeMs, float x, float y);
3252
3253    private native void nativeLongPress(
3254            long nativeContentViewCoreImpl, long timeMs, float x, float y);
3255
3256    private native void nativePinchBegin(
3257            long nativeContentViewCoreImpl, long timeMs, float x, float y);
3258
3259    private native void nativePinchEnd(long nativeContentViewCoreImpl, long timeMs);
3260
3261    private native void nativePinchBy(long nativeContentViewCoreImpl, long timeMs,
3262            float anchorX, float anchorY, float deltaScale);
3263
3264    private native void nativeSelectBetweenCoordinates(
3265            long nativeContentViewCoreImpl, float x1, float y1, float x2, float y2);
3266
3267    private native void nativeMoveCaret(long nativeContentViewCoreImpl, float x, float y);
3268
3269    private native void nativeResetGestureDetectors(long nativeContentViewCoreImpl);
3270
3271    private native void nativeIgnoreRemainingTouchEvents(long nativeContentViewCoreImpl);
3272
3273    private native void nativeOnWindowFocusLost(long nativeContentViewCoreImpl);
3274
3275    private native void nativeSetDoubleTapSupportForPageEnabled(
3276            long nativeContentViewCoreImpl, boolean enabled);
3277    private native void nativeSetDoubleTapSupportEnabled(
3278            long nativeContentViewCoreImpl, boolean enabled);
3279    private native void nativeSetMultiTouchZoomSupportEnabled(
3280            long nativeContentViewCoreImpl, boolean enabled);
3281
3282    private native void nativeLoadIfNecessary(long nativeContentViewCoreImpl);
3283    private native void nativeRequestRestoreLoad(long nativeContentViewCoreImpl);
3284
3285    private native void nativeStopLoading(long nativeContentViewCoreImpl);
3286
3287    private native void nativeReload(long nativeContentViewCoreImpl, boolean checkForRepost);
3288    private native void nativeReloadIgnoringCache(
3289            long nativeContentViewCoreImpl, boolean checkForRepost);
3290
3291    private native void nativeCancelPendingReload(long nativeContentViewCoreImpl);
3292
3293    private native void nativeContinuePendingReload(long nativeContentViewCoreImpl);
3294
3295    private native void nativeSelectPopupMenuItems(long nativeContentViewCoreImpl, int[] indices);
3296
3297    private native void nativeScrollFocusedEditableNodeIntoView(long nativeContentViewCoreImpl);
3298
3299    private native void nativeClearHistory(long nativeContentViewCoreImpl);
3300
3301    private native void nativeEvaluateJavaScript(long nativeContentViewCoreImpl,
3302            String script, JavaScriptCallback callback, boolean startRenderer);
3303
3304    private native long nativeGetNativeImeAdapter(long nativeContentViewCoreImpl);
3305
3306    private native int nativeGetCurrentRenderProcessId(long nativeContentViewCoreImpl);
3307
3308    private native int nativeGetBackgroundColor(long nativeContentViewCoreImpl);
3309
3310    private native void nativeOnShow(long nativeContentViewCoreImpl);
3311    private native void nativeOnHide(long nativeContentViewCoreImpl);
3312
3313    private native void nativeSetUseDesktopUserAgent(long nativeContentViewCoreImpl,
3314            boolean enabled, boolean reloadOnChange);
3315    private native boolean nativeGetUseDesktopUserAgent(long nativeContentViewCoreImpl);
3316
3317    private native void nativeClearSslPreferences(long nativeContentViewCoreImpl);
3318
3319    private native void nativeSetAllowJavascriptInterfacesInspection(
3320            long nativeContentViewCoreImpl, boolean allow);
3321
3322    private native void nativeAddJavascriptInterface(long nativeContentViewCoreImpl, Object object,
3323            String name, Class requiredAnnotation, HashSet<Object> retainedObjectSet);
3324
3325    private native void nativeRemoveJavascriptInterface(long nativeContentViewCoreImpl,
3326            String name);
3327
3328    private native int nativeGetNavigationHistory(long nativeContentViewCoreImpl, Object context);
3329    private native void nativeGetDirectedNavigationHistory(long nativeContentViewCoreImpl,
3330            Object context, boolean isForward, int maxEntries);
3331    private native String nativeGetOriginalUrlForActiveNavigationEntry(
3332            long nativeContentViewCoreImpl);
3333
3334    private native void nativeUpdateVSyncParameters(long nativeContentViewCoreImpl,
3335            long timebaseMicros, long intervalMicros);
3336
3337    private native void nativeOnVSync(long nativeContentViewCoreImpl, long frameTimeMicros);
3338
3339    private native boolean nativeOnAnimate(long nativeContentViewCoreImpl, long frameTimeMicros);
3340
3341    private native void nativeWasResized(long nativeContentViewCoreImpl);
3342
3343    private native boolean nativeIsRenderWidgetHostViewReady(long nativeContentViewCoreImpl);
3344
3345    private native void nativeExitFullscreen(long nativeContentViewCoreImpl);
3346    private native void nativeUpdateTopControlsState(long nativeContentViewCoreImpl,
3347            boolean enableHiding, boolean enableShowing, boolean animate);
3348
3349    private native void nativeShowImeIfNeeded(long nativeContentViewCoreImpl);
3350
3351    private native void nativeSetAccessibilityEnabled(
3352            long nativeContentViewCoreImpl, boolean enabled);
3353
3354    private native void nativeExtractSmartClipData(long nativeContentViewCoreImpl,
3355            int x, int y, int w, int h);
3356}
3357