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