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