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