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