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