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