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