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