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