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