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.
5 package org.chromium.content.browser;
7 import android.annotation.SuppressLint;
8 import android.app.Activity;
9 import android.app.SearchManager;
10 import android.content.ClipboardManager;
11 import android.content.ContentResolver;
12 import android.content.Context;
13 import android.content.Intent;
14 import android.content.pm.PackageManager;
15 import android.content.res.Configuration;
16 import android.database.ContentObserver;
17 import android.graphics.Bitmap;
18 import android.graphics.Canvas;
19 import android.graphics.Rect;
20 import android.net.Uri;
21 import android.os.Build;
22 import android.os.Bundle;
23 import android.os.Handler;
24 import android.os.ResultReceiver;
25 import android.os.SystemClock;
26 import android.provider.Browser;
27 import android.provider.Settings;
28 import android.text.Editable;
29 import android.text.Selection;
30 import android.text.TextUtils;
31 import android.util.Log;
32 import android.util.Pair;
33 import android.view.ActionMode;
34 import android.view.HapticFeedbackConstants;
35 import android.view.InputDevice;
36 import android.view.KeyEvent;
37 import android.view.MotionEvent;
38 import android.view.View;
39 import android.view.ViewGroup;
40 import android.view.accessibility.AccessibilityEvent;
41 import android.view.accessibility.AccessibilityManager;
42 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
43 import android.view.accessibility.AccessibilityNodeInfo;
44 import android.view.accessibility.AccessibilityNodeProvider;
45 import android.view.inputmethod.EditorInfo;
46 import android.view.inputmethod.InputConnection;
47 import android.view.inputmethod.InputMethodManager;
48 import android.widget.FrameLayout;
50 import com.google.common.annotations.VisibleForTesting;
52 import org.chromium.base.ApiCompatibilityUtils;
53 import org.chromium.base.CalledByNative;
54 import org.chromium.base.CommandLine;
55 import org.chromium.base.JNINamespace;
56 import org.chromium.base.ObserverList;
57 import org.chromium.base.ObserverList.RewindableIterator;
58 import org.chromium.base.TraceEvent;
59 import org.chromium.content.R;
60 import org.chromium.content.browser.ScreenOrientationListener.ScreenOrientationObserver;
61 import org.chromium.content.browser.accessibility.AccessibilityInjector;
62 import org.chromium.content.browser.accessibility.BrowserAccessibilityManager;
63 import org.chromium.content.browser.input.AdapterInputConnection;
64 import org.chromium.content.browser.input.GamepadList;
65 import org.chromium.content.browser.input.ImeAdapter;
66 import org.chromium.content.browser.input.ImeAdapter.AdapterInputConnectionFactory;
67 import org.chromium.content.browser.input.InputMethodManagerWrapper;
68 import org.chromium.content.browser.input.PastePopupMenu;
69 import org.chromium.content.browser.input.PastePopupMenu.PastePopupMenuDelegate;
70 import org.chromium.content.browser.input.PopupTouchHandleDrawable;
71 import org.chromium.content.browser.input.PopupTouchHandleDrawable.PopupTouchHandleDrawableDelegate;
72 import org.chromium.content.browser.input.SelectPopup;
73 import org.chromium.content.browser.input.SelectPopupDialog;
74 import org.chromium.content.browser.input.SelectPopupDropdown;
75 import org.chromium.content.browser.input.SelectPopupItem;
76 import org.chromium.content.browser.input.SelectionEventType;
77 import org.chromium.content.common.ContentSwitches;
78 import org.chromium.content_public.browser.GestureStateListener;
79 import org.chromium.content_public.browser.JavaScriptCallback;
80 import org.chromium.content_public.browser.WebContents;
81 import org.chromium.ui.base.DeviceFormFactor;
82 import org.chromium.ui.base.ViewAndroid;
83 import org.chromium.ui.base.ViewAndroidDelegate;
84 import org.chromium.ui.base.WindowAndroid;
85 import org.chromium.ui.gfx.DeviceDisplayInfo;
87 import java.lang.annotation.Annotation;
88 import java.lang.reflect.Field;
89 import java.util.ArrayList;
90 import java.util.HashMap;
91 import java.util.HashSet;
92 import java.util.List;
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.
100 @JNINamespace("content")
101 public class ContentViewCore
102 implements NavigationClient, AccessibilityStateChangeListener, ScreenOrientationObserver {
104 private static final String TAG = "ContentViewCore";
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;
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;
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>>();
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>();
138 * Interface that consumers of {@link ContentViewCore} must implement to allow the proper
139 * dispatching of view methods through the containing view.
142 * All methods with the "super_" prefix should be routed to the parent of the
143 * implementing container view.
145 @SuppressWarnings("javadoc")
146 public interface InternalAccessDelegate {
148 * @see View#drawChild(Canvas, View, long)
150 boolean drawChild(Canvas canvas, View child, long drawingTime);
153 * @see View#onKeyUp(keyCode, KeyEvent)
155 boolean super_onKeyUp(int keyCode, KeyEvent event);
158 * @see View#dispatchKeyEventPreIme(KeyEvent)
160 boolean super_dispatchKeyEventPreIme(KeyEvent event);
163 * @see View#dispatchKeyEvent(KeyEvent)
165 boolean super_dispatchKeyEvent(KeyEvent event);
168 * @see View#onGenericMotionEvent(MotionEvent)
170 boolean super_onGenericMotionEvent(MotionEvent event);
173 * @see View#onConfigurationChanged(Configuration)
175 void super_onConfigurationChanged(Configuration newConfig);
178 * @see View#onScrollChanged(int, int, int, int)
180 void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix);
183 * @see View#awakenScrollBars()
185 boolean awakenScrollBars();
188 * @see View#awakenScrollBars(int, boolean)
190 boolean super_awakenScrollBars(int startDelay, boolean invalidate);
194 * An interface for controlling visibility and state of embedder-provided zoom controls.
196 public interface ZoomControlsDelegate {
198 * Called when it's reasonable to show zoom controls.
200 void invokeZoomPicker();
203 * Called when zoom controls need to be hidden (e.g. when the view hides).
205 void dismissZoomPicker();
208 * Called when page scale has been changed, so the controls can update their state.
210 void updateZoomControls();
214 * An interface that allows the embedder to be notified when the results of
215 * extractSmartClipData are available.
217 public interface SmartClipDataListener {
218 public void onSmartClipDataExtracted(String text, String html, Rect clipRect);
221 private final Context mContext;
222 private ViewGroup mContainerView;
223 private InternalAccessDelegate mContainerViewInternals;
224 private WebContents mWebContents;
225 private WebContentsObserverAndroid mWebContentsObserver;
227 private ContentViewClient mContentViewClient;
229 private ContentSettings mContentSettings;
231 // Native pointer to C++ ContentViewCoreImpl object which will be set by nativeInit().
232 private long mNativeContentViewCore = 0;
234 private final ObserverList<GestureStateListener> mGestureStateListeners;
235 private final RewindableIterator<GestureStateListener> mGestureStateListenersIterator;
236 private ZoomControlsDelegate mZoomControlsDelegate;
238 private PopupZoomer mPopupZoomer;
239 private SelectPopup mSelectPopup;
241 private Runnable mFakeMouseMoveRunnable = null;
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;
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;
254 private PopupTouchHandleDrawableDelegate mTouchHandleDelegate;
256 private PositionObserver mPositionObserver;
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 mOverdrawBottomHeightPix;
264 private int mViewportSizeOffsetWidthPix;
265 private int mViewportSizeOffsetHeightPix;
267 // Cached copy of all positions and scales as reported by the renderer.
268 private final RenderCoordinates mRenderCoordinates;
270 // Tracks whether a selection is currently active. When applied to selected text, indicates
271 // whether the last selected text is still highlighted.
272 private boolean mHasSelection;
273 private boolean mHasInsertion;
274 private String mLastSelectedText;
275 private boolean mFocusedNodeEditable;
276 private ActionMode mActionMode;
277 private boolean mUnselectAllOnActionModeDismiss;
279 // Delegate that will handle GET downloads, and be notified of completion of POST downloads.
280 private ContentViewDownloadDelegate mDownloadDelegate;
282 // The AccessibilityInjector that handles loading Accessibility scripts into the web page.
283 private AccessibilityInjector mAccessibilityInjector;
285 // Whether native accessibility, i.e. without any script injection, is allowed.
286 private boolean mNativeAccessibilityAllowed;
288 // Whether native accessibility, i.e. without any script injection, has been enabled.
289 private boolean mNativeAccessibilityEnabled;
291 // Handles native accessibility, i.e. without any script injection.
292 private BrowserAccessibilityManager mBrowserAccessibilityManager;
294 // System accessibility service.
295 private final AccessibilityManager mAccessibilityManager;
297 // Accessibility touch exploration state.
298 private boolean mTouchExplorationEnabled;
300 // Allows us to dynamically respond when the accessibility script injection flag changes.
301 private ContentObserver mAccessibilityScriptInjectionObserver;
303 // Temporary notification to tell onSizeChanged to focus a form element,
304 // because the OSK was just brought up.
305 private final Rect mFocusPreOSKViewportRect = new Rect();
307 // On tap this will store the x, y coordinates of the touch.
308 private int mLastTapX;
309 private int mLastTapY;
311 // Whether a touch scroll sequence is active, used to hide text selection
312 // handles. Note that a scroll sequence will *always* bound a pinch
313 // sequence, so this will also be true for the duration of a pinch gesture.
314 private boolean mTouchScrollInProgress;
316 // The outstanding fling start events that hasn't got fling end yet. It may be > 1 because
317 // onNativeFlingStopped() is called asynchronously.
318 private int mPotentiallyActiveFlingCount;
320 private ViewAndroid mViewAndroid;
322 private SmartClipDataListener mSmartClipDataListener = null;
324 // This holds the state of editable text (e.g. contents of <input>, contenteditable) of
325 // a focused element.
326 // Every time the user, IME, javascript (Blink), autofill etc. modifies the content, the new
327 // state must be reflected to this to keep consistency.
328 private final Editable mEditable;
331 * PID used to indicate an invalid render process.
333 // Keep in sync with the value returned from ContentViewCoreImpl::GetCurrentRendererProcessId()
334 // if there is no render process.
335 public static final int INVALID_RENDER_PROCESS_PID = 0;
337 // Offsets for the events that passes through this ContentViewCore.
338 private float mCurrentTouchOffsetX;
339 private float mCurrentTouchOffsetY;
341 // Offsets for smart clip
342 private int mSmartClipOffsetX;
343 private int mSmartClipOffsetY;
345 // Whether the ContentViewCore requires the WebContents to be fullscreen in order to lock the
346 // screen orientation.
347 private boolean mFullscreenRequiredForOrientationLock = true;
350 * Constructs a new ContentViewCore. Embedders must call initialize() after constructing
351 * a ContentViewCore and before using it.
353 * @param context The context used to create this.
355 public ContentViewCore(Context context) {
358 mAdapterInputConnectionFactory = new AdapterInputConnectionFactory();
359 mInputMethodManagerWrapper = new InputMethodManagerWrapper(mContext);
361 mRenderCoordinates = new RenderCoordinates();
362 float deviceScaleFactor = getContext().getResources().getDisplayMetrics().density;
363 String forceScaleFactor = CommandLine.getInstance().getSwitchValue(
364 ContentSwitches.FORCE_DEVICE_SCALE_FACTOR);
365 if (forceScaleFactor != null) {
366 deviceScaleFactor = Float.valueOf(forceScaleFactor);
368 mRenderCoordinates.setDeviceScaleFactor(deviceScaleFactor);
369 mAccessibilityManager = (AccessibilityManager)
370 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
371 mGestureStateListeners = new ObserverList<GestureStateListener>();
372 mGestureStateListenersIterator = mGestureStateListeners.rewindableIterator();
374 mEditable = Editable.Factory.getInstance().newEditable("");
375 Selection.setSelection(mEditable, 0);
379 * @return The context used for creating this ContentViewCore.
382 public Context getContext() {
387 * @return The ViewGroup that all view actions of this ContentViewCore should interact with.
389 public ViewGroup getContainerView() {
390 return mContainerView;
394 * @return The WebContents currently being rendered.
396 public WebContents getWebContents() {
401 * Specifies how much smaller the WebKit layout size should be relative to the size of this
403 * @param offsetXPix The X amount in pixels to shrink the viewport by.
404 * @param offsetYPix The Y amount in pixels to shrink the viewport by.
406 public void setViewportSizeOffset(int offsetXPix, int offsetYPix) {
407 if (offsetXPix != mViewportSizeOffsetWidthPix ||
408 offsetYPix != mViewportSizeOffsetHeightPix) {
409 mViewportSizeOffsetWidthPix = offsetXPix;
410 mViewportSizeOffsetHeightPix = offsetYPix;
411 if (mNativeContentViewCore != 0) nativeWasResized(mNativeContentViewCore);
416 * Returns a delegate that can be used to add and remove views from the ContainerView.
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
424 * @return A ViewAndroidDelegate that can be used to add and remove views.
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;
434 public View acquireAnchorView() {
435 View anchorView = new View(mContext);
436 mContainerViewAtCreation.addView(anchorView);
441 @SuppressWarnings("deprecation") // AbsoluteLayout
442 public void setAnchorViewPosition(
443 View view, float x, float y, float width, float height) {
444 assert view.getParent() == mContainerViewAtCreation;
446 float scale = (float) DeviceDisplayInfo.create(mContext).getDIPScale();
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) {
455 if (ApiCompatibilityUtils.isLayoutRtl(mContainerViewAtCreation)) {
456 startMargin = mContainerViewAtCreation.getMeasuredWidth()
457 - Math.round((width + x) * scale);
459 startMargin = leftMargin;
461 if (scaledWidth + startMargin > mContainerViewAtCreation.getWidth()) {
462 scaledWidth = mContainerViewAtCreation.getWidth() - startMargin;
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
475 leftMargin += mRenderCoordinates.getScrollXPixInt();
476 topMargin += mRenderCoordinates.getScrollYPixInt();
478 android.widget.AbsoluteLayout.LayoutParams lp =
479 new android.widget.AbsoluteLayout.LayoutParams(
480 scaledWidth, (int) (height * scale), leftMargin, topMargin);
481 view.setLayoutParams(lp);
483 Log.e(TAG, "Unknown layout " + mContainerViewAtCreation.getClass().getName());
488 public void releaseAnchorView(View anchorView) {
489 mContainerViewAtCreation.removeView(anchorView);
495 public void setImeAdapterForTest(ImeAdapter imeAdapter) {
496 mImeAdapter = imeAdapter;
500 public ImeAdapter getImeAdapterForTest() {
505 public void setAdapterInputConnectionFactory(AdapterInputConnectionFactory factory) {
506 mAdapterInputConnectionFactory = factory;
510 public void setInputMethodManagerWrapperForTest(InputMethodManagerWrapper immw) {
511 mInputMethodManagerWrapper = immw;
515 public AdapterInputConnection getInputConnectionForTest() {
516 return mInputConnection;
519 private ImeAdapter createImeAdapter(Context context) {
520 return new ImeAdapter(mInputMethodManagerWrapper,
521 new ImeAdapter.ImeAdapterDelegate() {
523 public void onImeEvent() {
524 if (mPopupZoomer.isShowing()) {
525 mPopupZoomer.hide(true);
527 getContentViewClient().onImeEvent();
532 public void onDismissInput() {
533 getContentViewClient().onImeStateChangeRequested(false);
537 public View getAttachedView() {
538 return mContainerView;
542 public ResultReceiver getNewShowKeyboardReceiver() {
543 return new ResultReceiver(new Handler()) {
545 public void onReceiveResult(int resultCode, Bundle resultData) {
546 getContentViewClient().onImeStateChangeRequested(
547 resultCode == InputMethodManager.RESULT_SHOWN ||
548 resultCode == InputMethodManager.RESULT_UNCHANGED_SHOWN);
549 if (resultCode == InputMethodManager.RESULT_SHOWN) {
550 // If OSK is newly shown, delay the form focus until
551 // the onSizeChanged (in order to adjust relative to the
553 // TODO(jdduke): We should not assume that onSizeChanged will
554 // always be called, crbug.com/294908.
555 getContainerView().getWindowVisibleDisplayFrame(
556 mFocusPreOSKViewportRect);
557 } else if (hasFocus() && resultCode ==
558 InputMethodManager.RESULT_UNCHANGED_SHOWN) {
559 // If the OSK was already there, focus the form immediately.
560 scrollFocusedEditableNodeIntoView();
571 * @param containerView The view that will act as a container for all views created by this.
572 * @param internalDispatcher Handles dispatching all hidden or super methods to the
574 * @param nativeWebContents A pointer to the native web contents.
575 * @param windowAndroid An instance of the WindowAndroid.
577 // Perform important post-construction set up of the ContentViewCore.
578 // We do not require the containing view in the constructor to allow embedders to create a
579 // ContentViewCore without having fully created its containing view. The containing view
580 // is a vital component of the ContentViewCore, so embedders must exercise caution in what
581 // they do with the ContentViewCore before calling initialize().
582 // We supply the nativeWebContents pointer here rather than in the constructor to allow us
583 // to set the private browsing mode at a later point for the WebView implementation.
584 // Note that the caller remains the owner of the nativeWebContents and is responsible for
585 // deleting it after destroying the ContentViewCore.
586 public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
587 long nativeWebContents, WindowAndroid windowAndroid) {
588 setContainerView(containerView);
590 long windowNativePointer = windowAndroid.getNativePointer();
591 assert windowNativePointer != 0;
592 mViewAndroid = new ViewAndroid(windowAndroid, getViewAndroidDelegate());
593 long viewAndroidNativePointer = mViewAndroid.getNativePointer();
594 assert viewAndroidNativePointer != 0;
596 mZoomControlsDelegate = new ZoomControlsDelegate() {
598 public void invokeZoomPicker() {}
600 public void dismissZoomPicker() {}
602 public void updateZoomControls() {}
605 mNativeContentViewCore = nativeInit(
606 nativeWebContents, viewAndroidNativePointer, windowNativePointer,
607 mRetainedJavaScriptObjects);
608 mWebContents = nativeGetWebContentsAndroid(mNativeContentViewCore);
609 mContentSettings = new ContentSettings(this, mNativeContentViewCore);
611 setContainerViewInternals(internalDispatcher);
612 mRenderCoordinates.reset();
613 initPopupZoomer(mContext);
614 mImeAdapter = createImeAdapter(mContext);
616 mAccessibilityInjector = AccessibilityInjector.newInstance(this);
618 mWebContentsObserver = new WebContentsObserverAndroid(mWebContents) {
620 public void didNavigateMainFrame(String url, String baseUrl,
621 boolean isNavigationToDifferentPage, boolean isFragmentNavigation) {
622 if (!isNavigationToDifferentPage) return;
624 resetScrollInProgress();
625 resetGestureDetection();
629 public void renderProcessGone(boolean wasOomProtected) {
631 resetScrollInProgress();
632 // No need to reset gesture detection as the detector will have
633 // been destroyed in the RenderWidgetHostView.
639 * Sets a new container view for this {@link ContentViewCore}.
641 * <p>WARNING: This is not a general purpose method and has been designed with WebView
642 * fullscreen in mind. Please be aware that it might not be appropriate for other use cases
643 * and that it has a number of limitations. For example the PopupZoomer only works with the
644 * container view with which this ContentViewCore has been initialized.
646 * <p>This method only performs a small part of replacing the container view and
647 * embedders are responsible for:
649 * <li>Disconnecting the old container view from this ContentViewCore</li>
650 * <li>Updating the InternalAccessDelegate</li>
651 * <li>Reconciling the state of this ContentViewCore with the new container view</li>
652 * <li>Tearing down and recreating the native GL rendering where appropriate</li>
656 public void setContainerView(ViewGroup containerView) {
658 if (mContainerView != null) {
659 mPastePopupMenu = null;
660 mInputConnection = null;
664 mContainerView = containerView;
665 mPositionObserver = new ViewPositionObserver(mContainerView);
666 String contentDescription = "Web View";
667 if (R.string.accessibility_content_view == 0) {
668 Log.w(TAG, "Setting contentDescription to 'Web View' as no value was specified.");
670 contentDescription = mContext.getResources().getString(
671 R.string.accessibility_content_view);
673 mContainerView.setContentDescription(contentDescription);
674 mContainerView.setWillNotDraw(false);
675 mContainerView.setClickable(true);
680 void onNativeContentViewCoreDestroyed(long nativeContentViewCore) {
681 assert nativeContentViewCore == mNativeContentViewCore;
682 mNativeContentViewCore = 0;
686 * Set the Container view Internals.
687 * @param internalDispatcher Handles dispatching all hidden or super methods to the
690 public void setContainerViewInternals(InternalAccessDelegate internalDispatcher) {
691 mContainerViewInternals = internalDispatcher;
694 private void initPopupZoomer(Context context) {
695 mPopupZoomer = new PopupZoomer(context);
696 mPopupZoomer.setOnVisibilityChangedListener(new PopupZoomer.OnVisibilityChangedListener() {
697 // mContainerView can change, but this OnVisibilityChangedListener can only be used
698 // to add and remove views from the mContainerViewAtCreation.
699 private final ViewGroup mContainerViewAtCreation = mContainerView;
702 public void onPopupZoomerShown(final PopupZoomer zoomer) {
703 mContainerViewAtCreation.post(new Runnable() {
706 if (mContainerViewAtCreation.indexOfChild(zoomer) == -1) {
707 mContainerViewAtCreation.addView(zoomer);
709 assert false : "PopupZoomer should never be shown without being hidden";
716 public void onPopupZoomerHidden(final PopupZoomer zoomer) {
717 mContainerViewAtCreation.post(new Runnable() {
720 if (mContainerViewAtCreation.indexOfChild(zoomer) != -1) {
721 mContainerViewAtCreation.removeView(zoomer);
722 mContainerViewAtCreation.invalidate();
724 assert false : "PopupZoomer should never be hidden without being shown";
730 // TODO(yongsheng): LONG_TAP is not enabled in PopupZoomer. So need to dispatch a LONG_TAP
731 // gesture if a user completes a tap on PopupZoomer UI after a LONG_PRESS gesture.
732 PopupZoomer.OnTapListener listener = new PopupZoomer.OnTapListener() {
733 // mContainerView can change, but this OnTapListener can only be used
734 // with the mContainerViewAtCreation.
735 private final ViewGroup mContainerViewAtCreation = mContainerView;
738 public boolean onSingleTap(View v, MotionEvent e) {
739 mContainerViewAtCreation.requestFocus();
740 if (mNativeContentViewCore != 0) {
741 nativeSingleTap(mNativeContentViewCore, e.getEventTime(), e.getX(), e.getY());
747 public boolean onLongPress(View v, MotionEvent e) {
748 if (mNativeContentViewCore != 0) {
749 nativeLongPress(mNativeContentViewCore, e.getEventTime(), e.getX(), e.getY());
754 mPopupZoomer.setOnTapListener(listener);
758 * Destroy the internal state of the ContentView. This method may only be
759 * called after the ContentView has been removed from the view system. No
760 * other methods may be called on this ContentView after this method has
763 public void destroy() {
764 if (mNativeContentViewCore != 0) {
765 nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore);
768 if (mViewAndroid != null) mViewAndroid.destroy();
769 mNativeContentViewCore = 0;
770 mContentSettings = null;
771 mJavaScriptInterfaces.clear();
772 mRetainedJavaScriptObjects.clear();
773 unregisterAccessibilityContentObserver();
774 mGestureStateListeners.clear();
775 ScreenOrientationListener.getInstance().removeObserver(this);
776 mPositionObserver.clearListener();
779 private void unregisterAccessibilityContentObserver() {
780 if (mAccessibilityScriptInjectionObserver == null) {
783 getContext().getContentResolver().unregisterContentObserver(
784 mAccessibilityScriptInjectionObserver);
785 mAccessibilityScriptInjectionObserver = null;
789 * Returns true initially, false after destroy() has been called.
790 * It is illegal to call any other public method after destroy().
792 public boolean isAlive() {
793 return mNativeContentViewCore != 0;
797 * This is only useful for passing over JNI to native code that requires ContentViewCore*.
798 * @return native ContentViewCore pointer.
801 public long getNativeContentViewCore() {
802 return mNativeContentViewCore;
805 public void setContentViewClient(ContentViewClient client) {
806 if (client == null) {
807 throw new IllegalArgumentException("The client can't be null.");
809 mContentViewClient = client;
813 public ContentViewClient getContentViewClient() {
814 if (mContentViewClient == null) {
815 // We use the Null Object pattern to avoid having to perform a null check in this class.
816 // We create it lazily because most of the time a client will be set almost immediately
817 // after ContentView is created.
818 mContentViewClient = new ContentViewClient();
819 // We don't set the native ContentViewClient pointer here on purpose. The native
820 // implementation doesn't mind a null delegate and using one is better than passing a
821 // Null Object, since we cut down on the number of JNI calls.
823 return mContentViewClient;
826 public int getBackgroundColor() {
827 assert mWebContents != null;
828 return mWebContents.getBackgroundColor();
832 private void onBackgroundColorChanged(int color) {
833 getContentViewClient().onBackgroundColorChanged(color);
837 * Load url without fixing up the url string. Consumers of ContentView are responsible for
838 * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
839 * off during user input).
841 * @param params Parameters for this load.
843 public void loadUrl(LoadUrlParams params) {
844 if (mNativeContentViewCore == 0) return;
846 nativeLoadUrl(mNativeContentViewCore,
849 params.mTransitionType,
850 params.getReferrer() != null ? params.getReferrer().getUrl() : null,
851 params.getReferrer() != null ? params.getReferrer().getPolicy() : 0,
852 params.mUaOverrideOption,
853 params.getExtraHeadersString(),
855 params.mBaseUrlForDataUrl,
856 params.mVirtualUrlForDataUrl,
857 params.mCanLoadLocalResources,
858 params.mIsRendererInitiated);
862 * Stops loading the current web contents.
864 public void stopLoading() {
865 assert mWebContents != null;
870 * Get the URL of the current page.
872 * @return The URL of the current page.
874 public String getUrl() {
875 assert mWebContents != null;
876 return mWebContents.getUrl();
880 * Get the title of the current page.
882 * @return The title of the current page.
884 public String getTitle() {
885 assert mWebContents != null;
886 return mWebContents.getTitle();
890 * Shows an interstitial page driven by the passed in delegate.
892 * @param url The URL being blocked by the interstitial.
893 * @param delegate The delegate handling the interstitial.
896 public void showInterstitialPage(
897 String url, InterstitialPageDelegateAndroid delegate) {
898 assert mWebContents != null;
899 mWebContents.showInterstitialPage(url, delegate.getNative());
903 * @return Whether the page is currently showing an interstitial, such as a bad HTTPS page.
905 public boolean isShowingInterstitialPage() {
906 assert mWebContents != null;
907 return mWebContents.isShowingInterstitialPage();
911 * @return Viewport width in physical pixels as set from onSizeChanged.
914 public int getViewportWidthPix() { return mViewportWidthPix; }
917 * @return Viewport height in physical pixels as set from onSizeChanged.
920 public int getViewportHeightPix() { return mViewportHeightPix; }
923 * @return Width of underlying physical surface.
926 public int getPhysicalBackingWidthPix() { return mPhysicalBackingWidthPix; }
929 * @return Height of underlying physical surface.
932 public int getPhysicalBackingHeightPix() { return mPhysicalBackingHeightPix; }
935 * @return Amount the output surface extends past the bottom of the window viewport.
938 public int getOverdrawBottomHeightPix() { return mOverdrawBottomHeightPix; }
941 * @return The amount to shrink the viewport relative to {@link #getViewportWidthPix()}.
944 public int getViewportSizeOffsetWidthPix() { return mViewportSizeOffsetWidthPix; }
947 * @return The amount to shrink the viewport relative to {@link #getViewportHeightPix()}.
950 public int getViewportSizeOffsetHeightPix() { return mViewportSizeOffsetHeightPix; }
953 * @see android.webkit.WebView#getContentHeight()
955 public float getContentHeightCss() {
956 return mRenderCoordinates.getContentHeightCss();
960 * @see android.webkit.WebView#getContentWidth()
962 public float getContentWidthCss() {
963 return mRenderCoordinates.getContentWidthCss();
966 // TODO(teddchoc): Remove all these navigation controller methods from here and have the
967 // embedders manage it.
969 * @return Whether the current WebContents has a previous navigation entry.
971 public boolean canGoBack() {
972 return mWebContents != null && mWebContents.getNavigationController().canGoBack();
976 * @return Whether the current WebContents has a navigation entry after the current one.
978 public boolean canGoForward() {
979 return mWebContents != null && mWebContents.getNavigationController().canGoForward();
983 * @param offset The offset into the navigation history.
984 * @return Whether we can move in history by given offset
986 public boolean canGoToOffset(int offset) {
987 return mWebContents != null &&
988 mWebContents.getNavigationController().canGoToOffset(offset);
992 * Navigates to the specified offset from the "current entry". Does nothing if the offset is out
994 * @param offset The offset into the navigation history.
996 public void goToOffset(int offset) {
997 if (mWebContents != null) mWebContents.getNavigationController().goToOffset(offset);
1001 public void goToNavigationIndex(int index) {
1002 if (mWebContents != null) {
1003 mWebContents.getNavigationController().goToNavigationIndex(index);
1008 * Goes to the navigation entry before the current one.
1010 public void goBack() {
1011 if (mWebContents != null) mWebContents.getNavigationController().goBack();
1015 * Goes to the navigation entry following the current one.
1017 public void goForward() {
1018 if (mWebContents != null) mWebContents.getNavigationController().goForward();
1022 * Loads the current navigation if there is a pending lazy load (after tab restore).
1024 public void loadIfNecessary() {
1025 if (mWebContents != null) mWebContents.getNavigationController().loadIfNecessary();
1029 * Requests the current navigation to be loaded upon the next call to loadIfNecessary().
1031 public void requestRestoreLoad() {
1032 if (mWebContents != null) mWebContents.getNavigationController().requestRestoreLoad();
1036 * Reload the current page.
1038 public void reload(boolean checkForRepost) {
1039 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1040 if (mWebContents != null) mWebContents.getNavigationController().reload(checkForRepost);
1044 * Reload the current page, ignoring the contents of the cache.
1046 public void reloadIgnoringCache(boolean checkForRepost) {
1047 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1048 if (mWebContents != null) mWebContents.getNavigationController().reloadIgnoringCache(
1053 * Cancel the pending reload.
1055 public void cancelPendingReload() {
1056 if (mWebContents != null) mWebContents.getNavigationController().cancelPendingReload();
1060 * Continue the pending reload.
1062 public void continuePendingReload() {
1063 if (mWebContents != null) mWebContents.getNavigationController().continuePendingReload();
1067 * Clears the ContentViewCore's page history in both the backwards and
1068 * forwards directions.
1070 public void clearHistory() {
1071 if (mNativeContentViewCore != 0) nativeClearHistory(mNativeContentViewCore);
1075 * @return The selected text (empty if no text selected).
1077 public String getSelectedText() {
1078 return mHasSelection ? mLastSelectedText : "";
1082 * @return Whether the current selection is editable (false if no text selected).
1084 public boolean isSelectionEditable() {
1085 return mHasSelection ? mFocusedNodeEditable : false;
1089 * @return Whether the current focused node is editable.
1091 public boolean isFocusedNodeEditable() {
1092 return mFocusedNodeEditable;
1095 // End FrameLayout overrides.
1098 * @see View#onTouchEvent(MotionEvent)
1100 public boolean onTouchEvent(MotionEvent event) {
1101 final boolean isTouchHandleEvent = false;
1102 return onTouchEventImpl(event, isTouchHandleEvent);
1105 private boolean onTouchEventImpl(MotionEvent event, boolean isTouchHandleEvent) {
1106 TraceEvent.begin("onTouchEvent");
1108 int eventAction = event.getActionMasked();
1110 if (eventAction == MotionEvent.ACTION_DOWN) {
1111 cancelRequestToScrollFocusedEditableNodeIntoView();
1114 if (SPenSupport.isSPenSupported(mContext))
1115 eventAction = SPenSupport.convertSPenEventAction(eventAction);
1116 if (!isValidTouchEventActionForNative(eventAction)) return false;
1118 if (mNativeContentViewCore == 0) return false;
1120 // A zero offset is quite common, in which case the unnecessary copy should be avoided.
1121 MotionEvent offset = null;
1122 if (mCurrentTouchOffsetX != 0 || mCurrentTouchOffsetY != 0) {
1123 offset = createOffsetMotionEvent(event);
1127 final int pointerCount = event.getPointerCount();
1128 final boolean consumed = nativeOnTouchEvent(mNativeContentViewCore, event,
1129 event.getEventTime(), eventAction,
1130 pointerCount, event.getHistorySize(), event.getActionIndex(),
1131 event.getX(), event.getY(),
1132 pointerCount > 1 ? event.getX(1) : 0,
1133 pointerCount > 1 ? event.getY(1) : 0,
1134 event.getPointerId(0), pointerCount > 1 ? event.getPointerId(1) : -1,
1135 event.getTouchMajor(), pointerCount > 1 ? event.getTouchMajor(1) : 0,
1136 event.getRawX(), event.getRawY(),
1137 event.getToolType(0),
1138 pointerCount > 1 ? event.getToolType(1) : MotionEvent.TOOL_TYPE_UNKNOWN,
1139 event.getButtonState(),
1140 isTouchHandleEvent);
1142 if (offset != null) offset.recycle();
1145 TraceEvent.end("onTouchEvent");
1149 private static boolean isValidTouchEventActionForNative(int eventAction) {
1150 // Only these actions have any effect on gesture detection. Other
1151 // actions have no corresponding WebTouchEvent type and may confuse the
1152 // touch pipline, so we ignore them entirely.
1153 return eventAction == MotionEvent.ACTION_DOWN
1154 || eventAction == MotionEvent.ACTION_UP
1155 || eventAction == MotionEvent.ACTION_CANCEL
1156 || eventAction == MotionEvent.ACTION_MOVE
1157 || eventAction == MotionEvent.ACTION_POINTER_DOWN
1158 || eventAction == MotionEvent.ACTION_POINTER_UP;
1161 public void setIgnoreRemainingTouchEvents() {
1162 resetGestureDetection();
1165 public boolean isScrollInProgress() {
1166 return mTouchScrollInProgress || mPotentiallyActiveFlingCount > 0;
1169 @SuppressWarnings("unused")
1171 private void onFlingStartEventConsumed(int vx, int vy) {
1172 mTouchScrollInProgress = false;
1173 mPotentiallyActiveFlingCount++;
1174 for (mGestureStateListenersIterator.rewind();
1175 mGestureStateListenersIterator.hasNext();) {
1176 mGestureStateListenersIterator.next().onFlingStartGesture(
1177 vx, vy, computeVerticalScrollOffset(), computeVerticalScrollExtent());
1181 @SuppressWarnings("unused")
1183 private void onFlingStartEventHadNoConsumer(int vx, int vy) {
1184 mTouchScrollInProgress = false;
1185 for (mGestureStateListenersIterator.rewind();
1186 mGestureStateListenersIterator.hasNext();) {
1187 mGestureStateListenersIterator.next().onUnhandledFlingStartEvent(vx, vy);
1191 @SuppressWarnings("unused")
1193 private void onFlingCancelEventAck() {
1194 updateGestureStateListener(GestureEventType.FLING_CANCEL);
1197 @SuppressWarnings("unused")
1199 private void onScrollBeginEventAck() {
1200 mTouchScrollInProgress = true;
1202 mZoomControlsDelegate.invokeZoomPicker();
1203 updateGestureStateListener(GestureEventType.SCROLL_START);
1206 @SuppressWarnings("unused")
1208 private void onScrollUpdateGestureConsumed() {
1209 mZoomControlsDelegate.invokeZoomPicker();
1210 for (mGestureStateListenersIterator.rewind();
1211 mGestureStateListenersIterator.hasNext();) {
1212 mGestureStateListenersIterator.next().onScrollUpdateGestureConsumed();
1216 @SuppressWarnings("unused")
1218 private void onScrollEndEventAck() {
1219 if (!mTouchScrollInProgress) return;
1220 mTouchScrollInProgress = false;
1221 updateGestureStateListener(GestureEventType.SCROLL_END);
1224 @SuppressWarnings("unused")
1226 private void onPinchBeginEventAck() {
1227 updateGestureStateListener(GestureEventType.PINCH_BEGIN);
1230 @SuppressWarnings("unused")
1232 private void onPinchEndEventAck() {
1233 updateGestureStateListener(GestureEventType.PINCH_END);
1236 @SuppressWarnings("unused")
1238 private void onSingleTapEventAck(boolean consumed, int x, int y) {
1239 for (mGestureStateListenersIterator.rewind();
1240 mGestureStateListenersIterator.hasNext();) {
1241 mGestureStateListenersIterator.next().onSingleTap(consumed, x, y);
1246 * Called just prior to a tap or press gesture being forwarded to the renderer.
1248 @SuppressWarnings("unused")
1250 private boolean filterTapOrPressEvent(int type, int x, int y) {
1251 if (type == GestureEventType.LONG_PRESS && offerLongPressToEmbedder()) {
1254 updateForTapOrPress(type, x, y);
1259 public void sendDoubleTapForTest(long timeMs, int x, int y) {
1260 if (mNativeContentViewCore == 0) return;
1261 nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1265 public void flingForTest(long timeMs, int x, int y, int velocityX, int velocityY) {
1266 if (mNativeContentViewCore == 0) return;
1267 nativeFlingCancel(mNativeContentViewCore, timeMs);
1268 nativeScrollBegin(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1269 nativeFlingStart(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1273 * Cancel any fling gestures active.
1274 * @param timeMs Current time (in milliseconds).
1276 public void cancelFling(long timeMs) {
1277 if (mNativeContentViewCore == 0) return;
1278 nativeFlingCancel(mNativeContentViewCore, timeMs);
1282 * Add a listener that gets alerted on gesture state changes.
1283 * @param listener Listener to add.
1285 public void addGestureStateListener(GestureStateListener listener) {
1286 mGestureStateListeners.addObserver(listener);
1290 * Removes a listener that was added to watch for gesture state changes.
1291 * @param listener Listener to remove.
1293 public void removeGestureStateListener(GestureStateListener listener) {
1294 mGestureStateListeners.removeObserver(listener);
1297 void updateGestureStateListener(int gestureType) {
1298 for (mGestureStateListenersIterator.rewind();
1299 mGestureStateListenersIterator.hasNext();) {
1300 GestureStateListener listener = mGestureStateListenersIterator.next();
1301 switch (gestureType) {
1302 case GestureEventType.PINCH_BEGIN:
1303 listener.onPinchStarted();
1305 case GestureEventType.PINCH_END:
1306 listener.onPinchEnded();
1308 case GestureEventType.FLING_END:
1309 listener.onFlingEndGesture(
1310 computeVerticalScrollOffset(),
1311 computeVerticalScrollExtent());
1313 case GestureEventType.FLING_CANCEL:
1314 listener.onFlingCancelGesture();
1316 case GestureEventType.SCROLL_START:
1317 listener.onScrollStarted(
1318 computeVerticalScrollOffset(),
1319 computeVerticalScrollExtent());
1321 case GestureEventType.SCROLL_END:
1322 listener.onScrollEnded(
1323 computeVerticalScrollOffset(),
1324 computeVerticalScrollExtent());
1333 * Inserts the provided markup sandboxed into the frame.
1335 public void setupTransitionView(String markup) {
1336 assert mWebContents != null;
1337 mWebContents.setupTransitionView(markup);
1341 * Hides transition elements specified by the selector, and activates any
1342 * exiting-transition stylesheets.
1344 public void beginExitTransition(String cssSelector) {
1345 assert mWebContents != null;
1346 mWebContents.beginExitTransition(cssSelector);
1350 * Requests the renderer insert a link to the specified stylesheet in the
1351 * main frame's document.
1353 public void addStyleSheetByURL(String url) {
1354 assert mWebContents != null;
1355 mWebContents.addStyleSheetByURL(url);
1359 * Injects the passed Javascript code in the current page and evaluates it.
1360 * If a result is required, pass in a callback.
1361 * Used in automation tests.
1363 * @param script The Javascript to execute.
1364 * @param callback The callback to be fired off when a result is ready. The script's
1365 * result will be json encoded and passed as the parameter, and the call
1366 * will be made on the main thread.
1367 * If no result is required, pass null.
1369 public void evaluateJavaScript(String script, JavaScriptCallback callback) {
1370 assert mWebContents != null;
1371 mWebContents.evaluateJavaScript(script, callback, false);
1375 * Injects the passed Javascript code in the current page and evaluates it.
1376 * If there is no page existing, a new one will be created.
1378 * @param script The Javascript to execute.
1380 public void evaluateJavaScriptEvenIfNotYetNavigated(String script) {
1381 assert mWebContents != null;
1382 mWebContents.evaluateJavaScript(script, null, true);
1386 * Post a message to a frame.
1387 * TODO(sgurun) also add support for transferring a message channel port.
1389 * @param frameName The name of the frame. If the name is null the message is posted
1390 * to the main frame.
1391 * @param message The message
1392 * @param sourceOrigin The source origin
1393 * @param targetOrigin The target origin
1395 public void postMessageToFrame(String frameName, String message,
1396 String sourceOrigin, String targetOrigin) {
1397 if (mNativeContentViewCore == 0) return;
1398 nativePostMessageToFrame(mNativeContentViewCore, frameName, message, sourceOrigin,
1403 * To be called when the ContentView is shown.
1405 public void onShow() {
1406 assert mWebContents != null;
1407 mWebContents.onShow();
1408 setAccessibilityState(mAccessibilityManager.isEnabled());
1412 * @return The ID of the renderer process that backs this tab or
1413 * {@link #INVALID_RENDER_PROCESS_PID} if there is none.
1415 public int getCurrentRenderProcessId() {
1416 return nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1420 * To be called when the ContentView is hidden.
1422 public void onHide() {
1423 assert mWebContents != null;
1425 setInjectedAccessibility(false);
1426 mWebContents.onHide();
1430 * Return the ContentSettings object used to retrieve the settings for this
1431 * ContentViewCore. For modifications, ChromeNativePreferences is to be used.
1432 * @return A ContentSettings object that can be used to retrieve this
1433 * ContentViewCore's settings.
1435 public ContentSettings getContentSettings() {
1436 return mContentSettings;
1439 private void hidePopups() {
1440 mUnselectAllOnActionModeDismiss = true;
1441 hideSelectActionBar();
1447 public void hideSelectActionBar() {
1448 if (mActionMode != null) {
1449 mActionMode.finish();
1454 public boolean isSelectActionBarShowing() {
1455 return mActionMode != null;
1458 private void resetGestureDetection() {
1459 if (mNativeContentViewCore == 0) return;
1460 nativeResetGestureDetection(mNativeContentViewCore);
1464 * @see View#onAttachedToWindow()
1466 @SuppressWarnings("javadoc")
1467 public void onAttachedToWindow() {
1468 setAccessibilityState(mAccessibilityManager.isEnabled());
1470 ScreenOrientationListener.getInstance().addObserver(this, mContext);
1471 GamepadList.onAttachedToWindow(mContext);
1475 * @see View#onDetachedFromWindow()
1477 @SuppressWarnings("javadoc")
1478 @SuppressLint("MissingSuperCall")
1479 public void onDetachedFromWindow() {
1480 setInjectedAccessibility(false);
1482 mZoomControlsDelegate.dismissZoomPicker();
1483 unregisterAccessibilityContentObserver();
1485 ScreenOrientationListener.getInstance().removeObserver(this);
1486 GamepadList.onDetachedFromWindow();
1490 * @see View#onVisibilityChanged(android.view.View, int)
1492 public void onVisibilityChanged(View changedView, int visibility) {
1493 if (visibility != View.VISIBLE) {
1494 mZoomControlsDelegate.dismissZoomPicker();
1499 * @see View#onCreateInputConnection(EditorInfo)
1501 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1502 if (!mImeAdapter.hasTextInputType()) {
1503 // Although onCheckIsTextEditor will return false in this case, the EditorInfo
1504 // is still used by the InputMethodService. Need to make sure the IME doesn't
1505 // enter fullscreen mode.
1506 outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
1508 mInputConnection = mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter,
1509 mEditable, outAttrs);
1510 return mInputConnection;
1514 public AdapterInputConnection getAdapterInputConnectionForTest() {
1515 return mInputConnection;
1519 public Editable getEditableForTest() {
1524 * @see View#onCheckIsTextEditor()
1526 public boolean onCheckIsTextEditor() {
1527 return mImeAdapter.hasTextInputType();
1531 * @see View#onConfigurationChanged(Configuration)
1533 @SuppressWarnings("javadoc")
1534 public void onConfigurationChanged(Configuration newConfig) {
1537 if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
1538 if (mNativeContentViewCore != 0) {
1539 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
1540 ImeAdapter.getTextInputTypeNone(), 0 /* no flags */);
1542 mInputMethodManagerWrapper.restartInput(mContainerView);
1544 mContainerViewInternals.super_onConfigurationChanged(newConfig);
1546 // To request layout has side effect, but it seems OK as it only happen in
1547 // onConfigurationChange and layout has to be changed in most case.
1548 mContainerView.requestLayout();
1553 * @see View#onSizeChanged(int, int, int, int)
1555 @SuppressWarnings("javadoc")
1556 public void onSizeChanged(int wPix, int hPix, int owPix, int ohPix) {
1557 if (getViewportWidthPix() == wPix && getViewportHeightPix() == hPix) return;
1559 mViewportWidthPix = wPix;
1560 mViewportHeightPix = hPix;
1561 if (mNativeContentViewCore != 0) {
1562 nativeWasResized(mNativeContentViewCore);
1565 updateAfterSizeChanged();
1569 * Called when the underlying surface the compositor draws to changes size.
1570 * This may be larger than the viewport size.
1572 public void onPhysicalBackingSizeChanged(int wPix, int hPix) {
1573 if (mPhysicalBackingWidthPix == wPix && mPhysicalBackingHeightPix == hPix) return;
1575 mPhysicalBackingWidthPix = wPix;
1576 mPhysicalBackingHeightPix = hPix;
1578 if (mNativeContentViewCore != 0) {
1579 nativeWasResized(mNativeContentViewCore);
1584 * Called when the amount the surface is overdrawing off the bottom has changed.
1585 * @param overdrawHeightPix The overdraw height.
1587 public void onOverdrawBottomHeightChanged(int overdrawHeightPix) {
1588 if (mOverdrawBottomHeightPix == overdrawHeightPix) return;
1590 mOverdrawBottomHeightPix = overdrawHeightPix;
1592 if (mNativeContentViewCore != 0) {
1593 nativeWasResized(mNativeContentViewCore);
1597 private void updateAfterSizeChanged() {
1598 mPopupZoomer.hide(false);
1600 // Execute a delayed form focus operation because the OSK was brought
1602 if (!mFocusPreOSKViewportRect.isEmpty()) {
1603 Rect rect = new Rect();
1604 getContainerView().getWindowVisibleDisplayFrame(rect);
1605 if (!rect.equals(mFocusPreOSKViewportRect)) {
1606 // Only assume the OSK triggered the onSizeChanged if width was preserved.
1607 if (rect.width() == mFocusPreOSKViewportRect.width()) {
1608 scrollFocusedEditableNodeIntoView();
1610 cancelRequestToScrollFocusedEditableNodeIntoView();
1615 private void cancelRequestToScrollFocusedEditableNodeIntoView() {
1616 // Zero-ing the rect will prevent |updateAfterSizeChanged()| from
1617 // issuing the delayed form focus event.
1618 mFocusPreOSKViewportRect.setEmpty();
1621 private void scrollFocusedEditableNodeIntoView() {
1622 assert mWebContents != null;
1623 mWebContents.scrollFocusedEditableNodeIntoView();
1627 * Selects the word around the caret, if any.
1628 * The caller can check if selection actually occurred by listening to OnSelectionChanged.
1630 public void selectWordAroundCaret() {
1631 assert mWebContents != null;
1632 mWebContents.selectWordAroundCaret();
1636 * @see View#onWindowFocusChanged(boolean)
1638 public void onWindowFocusChanged(boolean hasWindowFocus) {
1639 if (!hasWindowFocus) resetGestureDetection();
1642 public void onFocusChanged(boolean gainFocus) {
1645 cancelRequestToScrollFocusedEditableNodeIntoView();
1649 if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1653 * @see View#onKeyUp(int, KeyEvent)
1655 public boolean onKeyUp(int keyCode, KeyEvent event) {
1656 if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1657 mPopupZoomer.hide(true);
1660 return mContainerViewInternals.super_onKeyUp(keyCode, event);
1664 * @see View#dispatchKeyEventPreIme(KeyEvent)
1666 public boolean dispatchKeyEventPreIme(KeyEvent event) {
1669 return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1676 * @see View#dispatchKeyEvent(KeyEvent)
1678 public boolean dispatchKeyEvent(KeyEvent event) {
1679 if (GamepadList.dispatchKeyEvent(event)) return true;
1680 if (getContentViewClient().shouldOverrideKeyEvent(event)) {
1681 return mContainerViewInternals.super_dispatchKeyEvent(event);
1684 if (mImeAdapter.dispatchKeyEvent(event)) return true;
1686 return mContainerViewInternals.super_dispatchKeyEvent(event);
1690 * @see View#onHoverEvent(MotionEvent)
1691 * Mouse move events are sent on hover enter, hover move and hover exit.
1692 * They are sent on hover exit because sometimes it acts as both a hover
1693 * move and hover exit.
1695 public boolean onHoverEvent(MotionEvent event) {
1696 TraceEvent.begin("onHoverEvent");
1697 MotionEvent offset = createOffsetMotionEvent(event);
1699 if (mBrowserAccessibilityManager != null) {
1700 return mBrowserAccessibilityManager.onHoverEvent(offset);
1703 // Work around Android bug where the x, y coordinates of a hover exit
1704 // event are incorrect when touch exploration is on.
1705 if (mTouchExplorationEnabled && offset.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
1709 mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1710 if (mNativeContentViewCore != 0) {
1711 nativeSendMouseMoveEvent(mNativeContentViewCore, offset.getEventTime(),
1712 offset.getX(), offset.getY());
1717 TraceEvent.end("onHoverEvent");
1722 * @see View#onGenericMotionEvent(MotionEvent)
1724 public boolean onGenericMotionEvent(MotionEvent event) {
1725 if (GamepadList.onGenericMotionEvent(event)) return true;
1726 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1727 switch (event.getAction()) {
1728 case MotionEvent.ACTION_SCROLL:
1729 if (mNativeContentViewCore == 0) return false;
1731 nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
1732 event.getX(), event.getY(),
1733 event.getAxisValue(MotionEvent.AXIS_VSCROLL));
1735 mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1736 // Send a delayed onMouseMove event so that we end
1737 // up hovering over the right position after the scroll.
1738 final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event);
1739 mFakeMouseMoveRunnable = new Runnable() {
1742 onHoverEvent(eventFakeMouseMove);
1743 eventFakeMouseMove.recycle();
1746 mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1750 return mContainerViewInternals.super_onGenericMotionEvent(event);
1754 * Sets the current amount to offset incoming touch events by. This is used to handle content
1755 * moving and not lining up properly with the android input system.
1756 * @param dx The X offset in pixels to shift touch events.
1757 * @param dy The Y offset in pixels to shift touch events.
1759 public void setCurrentMotionEventOffsets(float dx, float dy) {
1760 mCurrentTouchOffsetX = dx;
1761 mCurrentTouchOffsetY = dy;
1764 private MotionEvent createOffsetMotionEvent(MotionEvent src) {
1765 MotionEvent dst = MotionEvent.obtain(src);
1766 dst.offsetLocation(mCurrentTouchOffsetX, mCurrentTouchOffsetY);
1771 * @see View#scrollBy(int, int)
1772 * Currently the ContentView scrolling happens in the native side. In
1773 * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
1774 * are overridden, so that View's mScrollX and mScrollY will be unchanged at
1775 * (0, 0). This is critical for drawing ContentView correctly.
1777 public void scrollBy(int xPix, int yPix) {
1778 if (mNativeContentViewCore != 0) {
1779 nativeScrollBy(mNativeContentViewCore,
1780 SystemClock.uptimeMillis(), 0, 0, xPix, yPix);
1785 * @see View#scrollTo(int, int)
1787 public void scrollTo(int xPix, int yPix) {
1788 if (mNativeContentViewCore == 0) return;
1789 final float xCurrentPix = mRenderCoordinates.getScrollXPix();
1790 final float yCurrentPix = mRenderCoordinates.getScrollYPix();
1791 final float dxPix = xPix - xCurrentPix;
1792 final float dyPix = yPix - yCurrentPix;
1793 if (dxPix != 0 || dyPix != 0) {
1794 long time = SystemClock.uptimeMillis();
1795 nativeScrollBegin(mNativeContentViewCore, time,
1796 xCurrentPix, yCurrentPix, -dxPix, -dyPix);
1797 nativeScrollBy(mNativeContentViewCore,
1798 time, xCurrentPix, yCurrentPix, dxPix, dyPix);
1799 nativeScrollEnd(mNativeContentViewCore, time);
1803 // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1805 public int getNativeScrollXForTest() {
1806 return mRenderCoordinates.getScrollXPixInt();
1809 // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1811 public int getNativeScrollYForTest() {
1812 return mRenderCoordinates.getScrollYPixInt();
1816 * @see View#computeHorizontalScrollExtent()
1818 @SuppressWarnings("javadoc")
1819 public int computeHorizontalScrollExtent() {
1820 return mRenderCoordinates.getLastFrameViewportWidthPixInt();
1824 * @see View#computeHorizontalScrollOffset()
1826 @SuppressWarnings("javadoc")
1827 public int computeHorizontalScrollOffset() {
1828 return mRenderCoordinates.getScrollXPixInt();
1832 * @see View#computeHorizontalScrollRange()
1834 @SuppressWarnings("javadoc")
1835 public int computeHorizontalScrollRange() {
1836 return mRenderCoordinates.getContentWidthPixInt();
1840 * @see View#computeVerticalScrollExtent()
1842 @SuppressWarnings("javadoc")
1843 public int computeVerticalScrollExtent() {
1844 return mRenderCoordinates.getLastFrameViewportHeightPixInt();
1848 * @see View#computeVerticalScrollOffset()
1850 @SuppressWarnings("javadoc")
1851 public int computeVerticalScrollOffset() {
1852 return mRenderCoordinates.getScrollYPixInt();
1856 * @see View#computeVerticalScrollRange()
1858 @SuppressWarnings("javadoc")
1859 public int computeVerticalScrollRange() {
1860 return mRenderCoordinates.getContentHeightPixInt();
1863 // End FrameLayout overrides.
1866 * @see View#awakenScrollBars(int, boolean)
1868 @SuppressWarnings("javadoc")
1869 public boolean awakenScrollBars(int startDelay, boolean invalidate) {
1870 // For the default implementation of ContentView which draws the scrollBars on the native
1871 // side, calling this function may get us into a bad state where we keep drawing the
1872 // scrollBars, so disable it by always returning false.
1873 if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
1876 return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
1880 private void updateForTapOrPress(int type, float xPix, float yPix) {
1881 if (type != GestureEventType.SINGLE_TAP_CONFIRMED
1882 && type != GestureEventType.SINGLE_TAP_UP
1883 && type != GestureEventType.LONG_PRESS
1884 && type != GestureEventType.LONG_TAP) {
1888 if (mContainerView.isFocusable() && mContainerView.isFocusableInTouchMode()
1889 && !mContainerView.isFocused()) {
1890 mContainerView.requestFocus();
1893 if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
1895 mLastTapX = (int) xPix;
1896 mLastTapY = (int) yPix;
1900 * @return The x coordinate for the last point that a tap or press gesture was initiated from.
1902 public int getLastTapX() {
1907 * @return The y coordinate for the last point that a tap or press gesture was initiated from.
1909 public int getLastTapY() {
1913 public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
1914 mZoomControlsDelegate = zoomControlsDelegate;
1917 public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
1918 if (mNativeContentViewCore == 0) return;
1919 nativeSetMultiTouchZoomSupportEnabled(mNativeContentViewCore, supportsMultiTouchZoom);
1922 public void updateDoubleTapSupport(boolean supportsDoubleTap) {
1923 if (mNativeContentViewCore == 0) return;
1924 nativeSetDoubleTapSupportEnabled(mNativeContentViewCore, supportsDoubleTap);
1927 public void selectPopupMenuItems(int[] indices) {
1928 if (mNativeContentViewCore != 0) {
1929 nativeSelectPopupMenuItems(mNativeContentViewCore, indices);
1931 mSelectPopup = null;
1935 * Send the screen orientation value to the renderer.
1938 void sendOrientationChangeEvent(int orientation) {
1939 if (mNativeContentViewCore == 0) return;
1941 nativeSendOrientationChangeEvent(mNativeContentViewCore, orientation);
1945 * Register the delegate to be used when content can not be handled by
1946 * the rendering engine, and should be downloaded instead. This will replace
1947 * the current delegate, if any.
1948 * @param delegate An implementation of ContentViewDownloadDelegate.
1950 public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
1951 mDownloadDelegate = delegate;
1954 // Called by DownloadController.
1955 ContentViewDownloadDelegate getDownloadDelegate() {
1956 return mDownloadDelegate;
1959 private void showSelectActionBar() {
1960 if (mActionMode != null) {
1961 mActionMode.invalidate();
1965 // Start a new action mode with a SelectActionModeCallback.
1966 SelectActionModeCallback.ActionHandler actionHandler =
1967 new SelectActionModeCallback.ActionHandler() {
1969 public void selectAll() {
1970 mImeAdapter.selectAll();
1979 public void copy() {
1984 public void paste() {
1985 mImeAdapter.paste();
1989 public void share() {
1990 final String query = getSelectedText();
1991 if (TextUtils.isEmpty(query)) return;
1993 Intent send = new Intent(Intent.ACTION_SEND);
1994 send.setType("text/plain");
1995 send.putExtra(Intent.EXTRA_TEXT, query);
1997 Intent i = Intent.createChooser(send, getContext().getString(
1998 R.string.actionbar_share));
1999 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2000 getContext().startActivity(i);
2001 } catch (android.content.ActivityNotFoundException ex) {
2002 // If no app handles it, do nothing.
2007 public void search() {
2008 final String query = getSelectedText();
2009 if (TextUtils.isEmpty(query)) return;
2011 // See if ContentViewClient wants to override
2012 if (getContentViewClient().doesPerformWebSearch()) {
2013 getContentViewClient().performWebSearch(query);
2017 Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
2018 i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2019 i.putExtra(SearchManager.QUERY, query);
2020 i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
2021 if (!(getContext() instanceof Activity)) {
2022 i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2025 getContext().startActivity(i);
2026 } catch (android.content.ActivityNotFoundException ex) {
2027 // If no app handles it, do nothing.
2032 public boolean isSelectionPassword() {
2033 return mImeAdapter.isSelectionPassword();
2037 public boolean isSelectionEditable() {
2038 return mFocusedNodeEditable;
2042 public void onDestroyActionMode() {
2044 if (mUnselectAllOnActionModeDismiss) {
2046 if (isSelectionEditable()) {
2047 int selectionEnd = Selection.getSelectionEnd(mEditable);
2048 mInputConnection.setSelection(selectionEnd, selectionEnd);
2050 mImeAdapter.unselect();
2053 getContentViewClient().onContextualActionBarHidden();
2057 public boolean isShareAvailable() {
2058 Intent intent = new Intent(Intent.ACTION_SEND);
2059 intent.setType("text/plain");
2060 return getContext().getPackageManager().queryIntentActivities(intent,
2061 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2065 public boolean isWebSearchAvailable() {
2066 if (getContentViewClient().doesPerformWebSearch()) return true;
2067 Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
2068 intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2069 return getContext().getPackageManager().queryIntentActivities(intent,
2070 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2074 // On ICS, startActionMode throws an NPE when getParent() is null.
2075 if (mContainerView.getParent() != null) {
2076 assert mWebContents != null;
2077 mActionMode = mContainerView.startActionMode(
2078 getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler,
2079 mWebContents.isIncognito()));
2081 mUnselectAllOnActionModeDismiss = true;
2082 if (mActionMode == null) {
2083 // There is no ActionMode, so remove the selection.
2084 mImeAdapter.unselect();
2086 getContentViewClient().onContextualActionBarShown();
2091 * Clears the current text selection.
2093 public void clearSelection() {
2094 mImeAdapter.unselect();
2097 private void hidePastePopup() {
2098 if (mPastePopupMenu == null) return;
2099 mPastePopupMenu.hide();
2103 private void onSelectionEvent(int eventType, float posXDip, float posYDip) {
2104 switch (eventType) {
2105 case SelectionEventType.SELECTION_SHOWN:
2106 mHasSelection = true;
2107 // TODO(cjhopman): Remove this when there is a better signal that long press caused
2108 // a selection. See http://crbug.com/150151.
2109 mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2110 showSelectActionBar();
2113 case SelectionEventType.SELECTION_CLEARED:
2114 mHasSelection = false;
2115 mUnselectAllOnActionModeDismiss = false;
2116 hideSelectActionBar();
2119 case SelectionEventType.SELECTION_DRAG_STARTED:
2122 case SelectionEventType.SELECTION_DRAG_STOPPED:
2125 case SelectionEventType.INSERTION_SHOWN:
2126 mHasInsertion = true;
2129 case SelectionEventType.INSERTION_MOVED:
2130 if (mPastePopupMenu == null) break;
2131 if (!isScrollInProgress() && mPastePopupMenu.isShowing()) {
2132 showPastePopup((int) posXDip, (int) posYDip);
2138 case SelectionEventType.INSERTION_TAPPED:
2139 if (mWasPastePopupShowingOnInsertionDragStart)
2142 showPastePopup((int) posXDip, (int) posYDip);
2145 case SelectionEventType.INSERTION_CLEARED:
2146 mHasInsertion = false;
2150 case SelectionEventType.INSERTION_DRAG_STARTED:
2151 mWasPastePopupShowingOnInsertionDragStart =
2152 mPastePopupMenu != null && mPastePopupMenu.isShowing();
2157 assert false : "Invalid selection event type.";
2159 getContentViewClient().onSelectionEvent(eventType);
2162 public boolean getUseDesktopUserAgent() {
2163 if (mNativeContentViewCore != 0) {
2164 return nativeGetUseDesktopUserAgent(mNativeContentViewCore);
2170 * Set whether or not we're using a desktop user agent for the currently loaded page.
2171 * @param override If true, use a desktop user agent. Use a mobile one otherwise.
2172 * @param reloadOnChange Reload the page if the UA has changed.
2174 public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange) {
2175 if (mNativeContentViewCore != 0) {
2176 nativeSetUseDesktopUserAgent(mNativeContentViewCore, override, reloadOnChange);
2180 public void clearSslPreferences() {
2181 if (mNativeContentViewCore != 0) nativeClearSslPreferences(mNativeContentViewCore);
2184 private void hideTextHandles() {
2185 mHasSelection = false;
2186 mHasInsertion = false;
2187 if (mNativeContentViewCore != 0) nativeHideTextHandles(mNativeContentViewCore);
2191 * Shows the IME if the focused widget could accept text input.
2193 public void showImeIfNeeded() {
2194 assert mWebContents != null;
2195 mWebContents.showImeIfNeeded();
2199 * Hides the IME if the containerView is the active view for IME.
2201 public void hideImeIfNeeded() {
2202 // Hide input method window from the current view synchronously
2203 // because ImeAdapter does so asynchronouly with a delay, and
2204 // by the time when ImeAdapter dismisses the input, the
2205 // containerView may have lost focus.
2206 // We cannot trust ContentViewClient#onImeStateChangeRequested to
2207 // hide the input window because it has an empty default implementation.
2208 // So we need to explicitly hide the input method window here.
2209 if (mInputMethodManagerWrapper.isActive(mContainerView)) {
2210 mInputMethodManagerWrapper.hideSoftInputFromWindow(
2211 mContainerView.getWindowToken(), 0, null);
2213 getContentViewClient().onImeStateChangeRequested(false);
2216 @SuppressWarnings("unused")
2218 private void updateFrameInfo(
2219 float scrollOffsetX, float scrollOffsetY,
2220 float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
2221 float contentWidth, float contentHeight,
2222 float viewportWidth, float viewportHeight,
2223 float controlsOffsetYCss, float contentOffsetYCss,
2224 float overdrawBottomHeightCss) {
2225 TraceEvent.begin("ContentViewCore:updateFrameInfo");
2226 // Adjust contentWidth/Height to be always at least as big as
2227 // the actual viewport (as set by onSizeChanged).
2228 final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2229 contentWidth = Math.max(contentWidth,
2230 mViewportWidthPix / (deviceScale * pageScaleFactor));
2231 contentHeight = Math.max(contentHeight,
2232 mViewportHeightPix / (deviceScale * pageScaleFactor));
2233 final float contentOffsetYPix = mRenderCoordinates.fromDipToPix(contentOffsetYCss);
2235 final boolean contentSizeChanged =
2236 contentWidth != mRenderCoordinates.getContentWidthCss()
2237 || contentHeight != mRenderCoordinates.getContentHeightCss();
2238 final boolean scaleLimitsChanged =
2239 minPageScaleFactor != mRenderCoordinates.getMinPageScaleFactor()
2240 || maxPageScaleFactor != mRenderCoordinates.getMaxPageScaleFactor();
2241 final boolean pageScaleChanged =
2242 pageScaleFactor != mRenderCoordinates.getPageScaleFactor();
2243 final boolean scrollChanged =
2245 || scrollOffsetX != mRenderCoordinates.getScrollX()
2246 || scrollOffsetY != mRenderCoordinates.getScrollY();
2247 final boolean contentOffsetChanged =
2248 contentOffsetYPix != mRenderCoordinates.getContentOffsetYPix();
2250 final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
2251 final boolean needUpdateZoomControls = scaleLimitsChanged || scrollChanged;
2253 if (needHidePopupZoomer) mPopupZoomer.hide(true);
2255 if (scrollChanged) {
2256 mContainerViewInternals.onScrollChanged(
2257 (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetX),
2258 (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetY),
2259 (int) mRenderCoordinates.getScrollXPix(),
2260 (int) mRenderCoordinates.getScrollYPix());
2263 mRenderCoordinates.updateFrameInfo(
2264 scrollOffsetX, scrollOffsetY,
2265 contentWidth, contentHeight,
2266 viewportWidth, viewportHeight,
2267 pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
2270 if (scrollChanged || contentOffsetChanged) {
2271 for (mGestureStateListenersIterator.rewind();
2272 mGestureStateListenersIterator.hasNext();) {
2273 mGestureStateListenersIterator.next().onScrollOffsetOrExtentChanged(
2274 computeVerticalScrollOffset(),
2275 computeVerticalScrollExtent());
2279 if (needUpdateZoomControls) mZoomControlsDelegate.updateZoomControls();
2281 // Update offsets for fullscreen.
2282 final float controlsOffsetPix = controlsOffsetYCss * deviceScale;
2283 final float overdrawBottomHeightPix = overdrawBottomHeightCss * deviceScale;
2284 getContentViewClient().onOffsetsForFullscreenChanged(
2285 controlsOffsetPix, contentOffsetYPix, overdrawBottomHeightPix);
2287 if (mBrowserAccessibilityManager != null) {
2288 mBrowserAccessibilityManager.notifyFrameInfoInitialized();
2290 TraceEvent.end("ContentViewCore:updateFrameInfo");
2294 private void updateImeAdapter(long nativeImeAdapterAndroid, int textInputType,
2295 int textInputFlags, String text, int selectionStart, int selectionEnd,
2296 int compositionStart, int compositionEnd, boolean showImeIfNeeded,
2297 boolean isNonImeChange) {
2299 mFocusedNodeEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
2300 if (!mFocusedNodeEditable) hidePastePopup();
2302 mImeAdapter.updateKeyboardVisibility(
2303 nativeImeAdapterAndroid, textInputType, textInputFlags, showImeIfNeeded);
2305 if (mInputConnection != null) {
2306 mInputConnection.updateState(text, selectionStart, selectionEnd, compositionStart,
2307 compositionEnd, isNonImeChange);
2310 if (mActionMode != null) mActionMode.invalidate();
2314 @SuppressWarnings("unused")
2316 private void setTitle(String title) {
2317 getContentViewClient().onUpdateTitle(title);
2321 * Called (from native) when the <select> popup needs to be shown.
2322 * @param items Items to show.
2323 * @param enabled POPUP_ITEM_TYPEs for items.
2324 * @param multiple Whether the popup menu should support multi-select.
2325 * @param selectedIndices Indices of selected items.
2327 @SuppressWarnings("unused")
2329 private void showSelectPopup(Rect bounds, String[] items, int[] enabled, boolean multiple,
2330 int[] selectedIndices) {
2331 if (mContainerView.getParent() == null || mContainerView.getVisibility() != View.VISIBLE) {
2332 selectPopupMenuItems(null);
2336 assert items.length == enabled.length;
2337 List<SelectPopupItem> popupItems = new ArrayList<SelectPopupItem>();
2338 for (int i = 0; i < items.length; i++) {
2339 popupItems.add(new SelectPopupItem(items[i], enabled[i]));
2342 if (DeviceFormFactor.isTablet(mContext) && !multiple) {
2343 mSelectPopup = new SelectPopupDropdown(this, popupItems, bounds, selectedIndices);
2345 mSelectPopup = new SelectPopupDialog(this, popupItems, multiple, selectedIndices);
2347 mSelectPopup.show();
2351 * Called when the <select> popup needs to be hidden.
2354 private void hideSelectPopup() {
2355 if (mSelectPopup != null) mSelectPopup.hide();
2359 * @return The visible select popup being shown.
2361 public SelectPopup getSelectPopupForTest() {
2362 return mSelectPopup;
2365 @SuppressWarnings("unused")
2367 private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
2368 mPopupZoomer.setBitmap(zoomedBitmap);
2369 mPopupZoomer.show(targetRect);
2372 @SuppressWarnings("unused")
2374 private TouchEventSynthesizer createTouchEventSynthesizer() {
2375 return new TouchEventSynthesizer(this);
2378 @SuppressWarnings("unused")
2380 private PopupTouchHandleDrawable createPopupTouchHandleDrawable() {
2381 if (mTouchHandleDelegate == null) {
2382 mTouchHandleDelegate = new PopupTouchHandleDrawableDelegate() {
2383 public View getParent() {
2384 return getContainerView();
2387 public PositionObserver getParentPositionObserver() {
2388 return mPositionObserver;
2391 public boolean onTouchHandleEvent(MotionEvent event) {
2392 final boolean isTouchHandleEvent = true;
2393 return onTouchEventImpl(event, isTouchHandleEvent);
2397 return new PopupTouchHandleDrawable(mTouchHandleDelegate);
2400 @SuppressWarnings("unused")
2402 private void onSelectionChanged(String text) {
2403 mLastSelectedText = text;
2404 getContentViewClient().onSelectionChanged(text);
2407 @SuppressWarnings("unused")
2409 private void showPastePopup(int xDip, int yDip) {
2410 if (!mHasInsertion || !canPaste()) return;
2411 final float contentOffsetYPix = mRenderCoordinates.getContentOffsetYPix();
2412 getPastePopup().showAt(
2413 (int) mRenderCoordinates.fromDipToPix(xDip),
2414 (int) (mRenderCoordinates.fromDipToPix(yDip) + contentOffsetYPix));
2417 private PastePopupMenu getPastePopup() {
2418 if (mPastePopupMenu == null) {
2419 mPastePopupMenu = new PastePopupMenu(getContainerView(),
2420 new PastePopupMenuDelegate() {
2421 public void paste() {
2422 mImeAdapter.paste();
2427 return mPastePopupMenu;
2430 private boolean canPaste() {
2431 if (!mFocusedNodeEditable) return false;
2432 return ((ClipboardManager) mContext.getSystemService(
2433 Context.CLIPBOARD_SERVICE)).hasPrimaryClip();
2436 @SuppressWarnings("unused")
2438 private void onRenderProcessChange() {
2443 * Attaches the native ImeAdapter object to the java ImeAdapter to allow communication via JNI.
2445 public void attachImeAdapter() {
2446 if (mImeAdapter != null && mNativeContentViewCore != 0) {
2447 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2452 * @see View#hasFocus()
2455 public boolean hasFocus() {
2456 return mContainerView.hasFocus();
2460 * Checks whether the ContentViewCore can be zoomed in.
2462 * @return True if the ContentViewCore can be zoomed in.
2464 // This method uses the term 'zoom' for legacy reasons, but relates
2465 // to what chrome calls the 'page scale factor'.
2466 public boolean canZoomIn() {
2467 final float zoomInExtent = mRenderCoordinates.getMaxPageScaleFactor()
2468 - mRenderCoordinates.getPageScaleFactor();
2469 return zoomInExtent > ZOOM_CONTROLS_EPSILON;
2473 * Checks whether the ContentViewCore can be zoomed out.
2475 * @return True if the ContentViewCore can be zoomed out.
2477 // This method uses the term 'zoom' for legacy reasons, but relates
2478 // to what chrome calls the 'page scale factor'.
2479 public boolean canZoomOut() {
2480 final float zoomOutExtent = mRenderCoordinates.getPageScaleFactor()
2481 - mRenderCoordinates.getMinPageScaleFactor();
2482 return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
2486 * Zooms in the ContentViewCore by 25% (or less if that would result in
2487 * zooming in more than possible).
2489 * @return True if there was a zoom change, false otherwise.
2491 // This method uses the term 'zoom' for legacy reasons, but relates
2492 // to what chrome calls the 'page scale factor'.
2493 public boolean zoomIn() {
2497 return pinchByDelta(1.25f);
2501 * Zooms out the ContentViewCore by 20% (or less if that would result in
2502 * zooming out more than possible).
2504 * @return True if there was a zoom change, false otherwise.
2506 // This method uses the term 'zoom' for legacy reasons, but relates
2507 // to what chrome calls the 'page scale factor'.
2508 public boolean zoomOut() {
2509 if (!canZoomOut()) {
2512 return pinchByDelta(0.8f);
2516 * Resets the zoom factor of the ContentViewCore.
2518 * @return True if there was a zoom change, false otherwise.
2520 // This method uses the term 'zoom' for legacy reasons, but relates
2521 // to what chrome calls the 'page scale factor'.
2522 public boolean zoomReset() {
2523 // The page scale factor is initialized to mNativeMinimumScale when
2524 // the page finishes loading. Thus sets it back to mNativeMinimumScale.
2525 if (!canZoomOut()) return false;
2526 return pinchByDelta(
2527 mRenderCoordinates.getMinPageScaleFactor()
2528 / mRenderCoordinates.getPageScaleFactor());
2532 * Simulate a pinch zoom gesture.
2534 * @param delta the factor by which the current page scale should be multiplied by.
2535 * @return whether the gesture was sent.
2537 public boolean pinchByDelta(float delta) {
2538 if (mNativeContentViewCore == 0) return false;
2540 long timeMs = SystemClock.uptimeMillis();
2541 int xPix = getViewportWidthPix() / 2;
2542 int yPix = getViewportHeightPix() / 2;
2544 nativePinchBegin(mNativeContentViewCore, timeMs, xPix, yPix);
2545 nativePinchBy(mNativeContentViewCore, timeMs, xPix, yPix, delta);
2546 nativePinchEnd(mNativeContentViewCore, timeMs);
2552 * Invokes the graphical zoom picker widget for this ContentView.
2554 public void invokeZoomPicker() {
2555 mZoomControlsDelegate.invokeZoomPicker();
2559 * Enables or disables inspection of JavaScript objects added via
2560 * {@link #addJavascriptInterface(Object, String)} by means of Object.keys() method and
2561 * "for .. in" loop. Being able to inspect JavaScript objects is useful
2562 * when debugging hybrid Android apps, but can't be enabled for legacy applications due
2563 * to compatibility risks.
2565 * @param allow Whether to allow JavaScript objects inspection.
2567 public void setAllowJavascriptInterfacesInspection(boolean allow) {
2568 nativeSetAllowJavascriptInterfacesInspection(mNativeContentViewCore, allow);
2572 * Returns JavaScript interface objects previously injected via
2573 * {@link #addJavascriptInterface(Object, String)}.
2575 * @return the mapping of names to interface objects and corresponding annotation classes
2577 public Map<String, Pair<Object, Class>> getJavascriptInterfaces() {
2578 return mJavaScriptInterfaces;
2582 * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
2583 * and automatically pass in {@link JavascriptInterface} as the required annotation.
2585 * @param object The Java object to inject into the ContentViewCore's JavaScript context. Null
2586 * values are ignored.
2587 * @param name The name used to expose the instance in JavaScript.
2589 public void addJavascriptInterface(Object object, String name) {
2590 addPossiblyUnsafeJavascriptInterface(object, name, JavascriptInterface.class);
2594 * This method injects the supplied Java object into the ContentViewCore.
2595 * The object is injected into the JavaScript context of the main frame,
2596 * using the supplied name. This allows the Java object to be accessed from
2597 * JavaScript. Note that that injected objects will not appear in
2598 * JavaScript until the page is next (re)loaded. For example:
2599 * <pre> view.addJavascriptInterface(new Object(), "injectedObject");
2600 * view.loadData("<!DOCTYPE html><title></title>", "text/html", null);
2601 * view.loadUrl("javascript:alert(injectedObject.toString())");</pre>
2602 * <p><strong>IMPORTANT:</strong>
2604 * <li> addJavascriptInterface() can be used to allow JavaScript to control
2605 * the host application. This is a powerful feature, but also presents a
2606 * security risk. Use of this method in a ContentViewCore containing
2607 * untrusted content could allow an attacker to manipulate the host
2608 * application in unintended ways, executing Java code with the permissions
2609 * of the host application. Use extreme care when using this method in a
2610 * ContentViewCore which could contain untrusted content. Particular care
2611 * should be taken to avoid unintentional access to inherited methods, such
2612 * as {@link Object#getClass()}. To prevent access to inherited methods,
2613 * pass an annotation for {@code requiredAnnotation}. This will ensure
2614 * that only methods with {@code requiredAnnotation} are exposed to the
2615 * Javascript layer. {@code requiredAnnotation} will be passed to all
2616 * subsequently injected Java objects if any methods return an object. This
2617 * means the same restrictions (or lack thereof) will apply. Alternatively,
2618 * {@link #addJavascriptInterface(Object, String)} can be called, which
2619 * automatically uses the {@link JavascriptInterface} annotation.
2620 * <li> JavaScript interacts with Java objects on a private, background
2621 * thread of the ContentViewCore. Care is therefore required to maintain
2622 * thread safety.</li>
2625 * @param object The Java object to inject into the
2626 * ContentViewCore's JavaScript context. Null
2627 * values are ignored.
2628 * @param name The name used to expose the instance in
2630 * @param requiredAnnotation Restrict exposed methods to ones with this
2631 * annotation. If {@code null} all methods are
2635 public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
2636 Class<? extends Annotation> requiredAnnotation) {
2637 if (mNativeContentViewCore != 0 && object != null) {
2638 mJavaScriptInterfaces.put(name, new Pair<Object, Class>(object, requiredAnnotation));
2639 nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation);
2644 * Removes a previously added JavaScript interface with the given name.
2646 * @param name The name of the interface to remove.
2648 public void removeJavascriptInterface(String name) {
2649 mJavaScriptInterfaces.remove(name);
2650 if (mNativeContentViewCore != 0) {
2651 nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2656 * Return the current scale of the ContentView.
2657 * @return The current page scale factor.
2659 public float getScale() {
2660 return mRenderCoordinates.getPageScaleFactor();
2664 * If the view is ready to draw contents to the screen. In hardware mode,
2665 * the initialization of the surface texture may not occur until after the
2666 * view has been added to the layout. This method will return {@code true}
2667 * once the texture is actually ready.
2669 public boolean isReady() {
2670 assert mWebContents != null;
2671 return mWebContents.isReady();
2675 private void startContentIntent(String contentUrl) {
2676 getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2680 public void onAccessibilityStateChanged(boolean enabled) {
2681 setAccessibilityState(enabled);
2685 * Determines whether or not this ContentViewCore can handle this accessibility action.
2686 * @param action The action to perform.
2687 * @return Whether or not this action is supported.
2689 public boolean supportsAccessibilityAction(int action) {
2690 return mAccessibilityInjector.supportsAccessibilityAction(action);
2694 * Attempts to perform an accessibility action on the web content. If the accessibility action
2695 * cannot be processed, it returns {@code null}, allowing the caller to know to call the
2696 * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value.
2697 * Otherwise the return value from this method should be used.
2698 * @param action The action to perform.
2699 * @param arguments Optional action arguments.
2700 * @return Whether the action was performed or {@code null} if the call should be delegated to
2701 * the super {@link View} class.
2703 public boolean performAccessibilityAction(int action, Bundle arguments) {
2704 if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2705 return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2712 * Set the BrowserAccessibilityManager, used for native accessibility
2713 * (not script injection). This is only set when system accessibility
2715 * @param manager The new BrowserAccessibilityManager.
2717 public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
2718 mBrowserAccessibilityManager = manager;
2722 * Get the BrowserAccessibilityManager, used for native accessibility
2723 * (not script injection). This will return null when system accessibility
2725 * @return This view's BrowserAccessibilityManager.
2727 public BrowserAccessibilityManager getBrowserAccessibilityManager() {
2728 return mBrowserAccessibilityManager;
2732 * If native accessibility (not script injection) is enabled, and if this is
2733 * running on JellyBean or later, returns an AccessibilityNodeProvider that
2734 * implements native accessibility for this view. Returns null otherwise.
2735 * Lazily initializes native accessibility here if it's allowed.
2736 * @return The AccessibilityNodeProvider, if available, or null otherwise.
2738 public AccessibilityNodeProvider getAccessibilityNodeProvider() {
2739 if (mBrowserAccessibilityManager != null) {
2740 return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
2743 if (mNativeAccessibilityAllowed &&
2744 !mNativeAccessibilityEnabled &&
2745 mNativeContentViewCore != 0 &&
2746 Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
2747 mNativeAccessibilityEnabled = true;
2748 nativeSetAccessibilityEnabled(mNativeContentViewCore, true);
2755 * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
2757 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2758 // Note: this is only used by the script-injecting accessibility code.
2759 mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
2763 * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
2765 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
2766 // Note: this is only used by the script-injecting accessibility code.
2767 event.setClassName(this.getClass().getName());
2769 // Identify where the top-left of the screen currently points to.
2770 event.setScrollX(mRenderCoordinates.getScrollXPixInt());
2771 event.setScrollY(mRenderCoordinates.getScrollYPixInt());
2773 // The maximum scroll values are determined by taking the content dimensions and
2774 // subtracting off the actual dimensions of the ChromeView.
2775 int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt());
2776 int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt());
2777 event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0);
2779 // Setting the maximum scroll values requires API level 15 or higher.
2780 final int SDK_VERSION_REQUIRED_TO_SET_SCROLL = 15;
2781 if (Build.VERSION.SDK_INT >= SDK_VERSION_REQUIRED_TO_SET_SCROLL) {
2782 event.setMaxScrollX(maxScrollXPix);
2783 event.setMaxScrollY(maxScrollYPix);
2788 * Returns whether accessibility script injection is enabled on the device
2790 public boolean isDeviceAccessibilityScriptInjectionEnabled() {
2792 // On JellyBean and higher, native accessibility is the default so script
2793 // injection is only allowed if enabled via a flag.
2794 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&
2795 !CommandLine.getInstance().hasSwitch(
2796 ContentSwitches.ENABLE_ACCESSIBILITY_SCRIPT_INJECTION)) {
2800 if (!mContentSettings.getJavaScriptEnabled()) {
2804 int result = getContext().checkCallingOrSelfPermission(
2805 android.Manifest.permission.INTERNET);
2806 if (result != PackageManager.PERMISSION_GRANTED) {
2810 Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
2811 field.setAccessible(true);
2812 String accessibilityScriptInjection = (String) field.get(null);
2813 ContentResolver contentResolver = getContext().getContentResolver();
2815 if (mAccessibilityScriptInjectionObserver == null) {
2816 ContentObserver contentObserver = new ContentObserver(new Handler()) {
2818 public void onChange(boolean selfChange, Uri uri) {
2819 setAccessibilityState(mAccessibilityManager.isEnabled());
2822 contentResolver.registerContentObserver(
2823 Settings.Secure.getUriFor(accessibilityScriptInjection),
2826 mAccessibilityScriptInjectionObserver = contentObserver;
2829 return Settings.Secure.getInt(contentResolver, accessibilityScriptInjection, 0) == 1;
2830 } catch (NoSuchFieldException e) {
2831 // Do nothing, default to false.
2832 } catch (IllegalAccessException e) {
2833 // Do nothing, default to false.
2839 * Returns whether or not accessibility injection is being used.
2841 public boolean isInjectingAccessibilityScript() {
2842 return mAccessibilityInjector.accessibilityIsAvailable();
2846 * Returns true if accessibility is on and touch exploration is enabled.
2848 public boolean isTouchExplorationEnabled() {
2849 return mTouchExplorationEnabled;
2853 * Turns browser accessibility on or off.
2854 * If |state| is |false|, this turns off both native and injected accessibility.
2855 * Otherwise, if accessibility script injection is enabled, this will enable the injected
2856 * accessibility scripts. Native accessibility is enabled on demand.
2858 public void setAccessibilityState(boolean state) {
2860 setInjectedAccessibility(false);
2861 mNativeAccessibilityAllowed = false;
2862 mTouchExplorationEnabled = false;
2864 boolean useScriptInjection = isDeviceAccessibilityScriptInjectionEnabled();
2865 setInjectedAccessibility(useScriptInjection);
2866 mNativeAccessibilityAllowed = !useScriptInjection;
2867 mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
2872 * Enable or disable injected accessibility features
2874 public void setInjectedAccessibility(boolean enabled) {
2875 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
2876 mAccessibilityInjector.setScriptEnabled(enabled);
2880 * Stop any TTS notifications that are currently going on.
2882 public void stopCurrentAccessibilityNotifications() {
2883 mAccessibilityInjector.onPageLostFocus();
2887 * Inform WebKit that Fullscreen mode has been exited by the user.
2889 public void exitFullscreen() {
2890 assert mWebContents != null;
2891 mWebContents.exitFullscreen();
2895 * Changes whether hiding the top controls is enabled.
2897 * @param enableHiding Whether hiding the top controls should be enabled or not.
2898 * @param enableShowing Whether showing the top controls should be enabled or not.
2899 * @param animate Whether the transition should be animated or not.
2901 public void updateTopControlsState(boolean enableHiding, boolean enableShowing,
2903 assert mWebContents != null;
2904 mWebContents.updateTopControlsState(
2905 enableHiding, enableShowing, animate);
2909 * Callback factory method for nativeGetNavigationHistory().
2912 private void addToNavigationHistory(Object history, int index, String url, String virtualUrl,
2913 String originalUrl, String title, Bitmap favicon) {
2914 NavigationEntry entry = new NavigationEntry(
2915 index, url, virtualUrl, originalUrl, title, favicon);
2916 ((NavigationHistory) history).addEntry(entry);
2920 * Get a copy of the navigation history of the view.
2922 public NavigationHistory getNavigationHistory() {
2923 NavigationHistory history = new NavigationHistory();
2924 if (mNativeContentViewCore != 0) {
2925 int currentIndex = nativeGetNavigationHistory(mNativeContentViewCore, history);
2926 history.setCurrentEntryIndex(currentIndex);
2932 public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
2933 NavigationHistory history = new NavigationHistory();
2934 if (mNativeContentViewCore != 0) {
2935 nativeGetDirectedNavigationHistory(
2936 mNativeContentViewCore, history, isForward, itemLimit);
2942 * @return The original request URL for the current navigation entry, or null if there is no
2945 public String getOriginalUrlForActiveNavigationEntry() {
2946 if (mNativeContentViewCore != 0) {
2947 return nativeGetOriginalUrlForActiveNavigationEntry(mNativeContentViewCore);
2953 * @return The cached copy of render positions and scales.
2955 public RenderCoordinates getRenderCoordinates() {
2956 return mRenderCoordinates;
2960 private static Rect createRect(int x, int y, int right, int bottom) {
2961 return new Rect(x, y, right, bottom);
2964 public void extractSmartClipData(int x, int y, int width, int height) {
2965 if (mNativeContentViewCore != 0) {
2966 x += mSmartClipOffsetX;
2967 y += mSmartClipOffsetY;
2968 nativeExtractSmartClipData(mNativeContentViewCore, x, y, width, height);
2973 * Set offsets for smart clip.
2975 * <p>This should be called if there is a viewport change introduced by,
2976 * e.g., show and hide of a location bar.
2978 * @param offsetX Offset for X position.
2979 * @param offsetY Offset for Y position.
2981 public void setSmartClipOffsets(int offsetX, int offsetY) {
2982 mSmartClipOffsetX = offsetX;
2983 mSmartClipOffsetY = offsetY;
2987 private void onSmartClipDataExtracted(String text, String html, Rect clipRect) {
2988 if (mSmartClipDataListener != null ) {
2989 mSmartClipDataListener.onSmartClipDataExtracted(text, html, clipRect);
2993 public void setSmartClipDataListener(SmartClipDataListener listener) {
2994 mSmartClipDataListener = listener;
2997 public void setBackgroundOpaque(boolean opaque) {
2998 if (mNativeContentViewCore != 0) {
2999 nativeSetBackgroundOpaque(mNativeContentViewCore, opaque);
3004 * Offer a long press gesture to the embedding View, primarily for WebView compatibility.
3006 * @return true if the embedder handled the event.
3008 private boolean offerLongPressToEmbedder() {
3009 return mContainerView.performLongClick();
3013 * Reset scroll and fling accounting, notifying listeners as appropriate.
3014 * This is useful as a failsafe when the input stream may have been interruped.
3016 private void resetScrollInProgress() {
3017 if (!isScrollInProgress()) return;
3019 final boolean touchScrollInProgress = mTouchScrollInProgress;
3020 final int potentiallyActiveFlingCount = mPotentiallyActiveFlingCount;
3022 mTouchScrollInProgress = false;
3023 mPotentiallyActiveFlingCount = 0;
3025 if (touchScrollInProgress) updateGestureStateListener(GestureEventType.SCROLL_END);
3026 if (potentiallyActiveFlingCount > 0) updateGestureStateListener(GestureEventType.FLING_END);
3029 private native long nativeInit(long webContentsPtr,
3030 long viewAndroidPtr, long windowAndroidPtr, HashSet<Object> retainedObjectSet);
3033 private ContentVideoViewClient getContentVideoViewClient() {
3034 return getContentViewClient().getContentVideoViewClient();
3038 private boolean shouldBlockMediaRequest(String url) {
3039 return getContentViewClient().shouldBlockMediaRequest(url);
3043 private void onNativeFlingStopped() {
3044 // Note that mTouchScrollInProgress should normally be false at this
3045 // point, but we reset it anyway as another failsafe.
3046 mTouchScrollInProgress = false;
3047 if (mPotentiallyActiveFlingCount <= 0) return;
3048 mPotentiallyActiveFlingCount--;
3049 updateGestureStateListener(GestureEventType.FLING_END);
3053 public void onScreenOrientationChanged(int orientation) {
3054 sendOrientationChangeEvent(orientation);
3057 public void resumeResponseDeferredAtStart() {
3058 assert mWebContents != null;
3059 mWebContents.resumeResponseDeferredAtStart();
3063 * Set whether the ContentViewCore requires the WebContents to be fullscreen in order to lock
3064 * the screen orientation.
3066 public void setFullscreenRequiredForOrientationLock(boolean value) {
3067 mFullscreenRequiredForOrientationLock = value;
3071 private boolean isFullscreenRequiredForOrientationLock() {
3072 return mFullscreenRequiredForOrientationLock;
3075 private native WebContents nativeGetWebContentsAndroid(long nativeContentViewCoreImpl);
3077 private native void nativeOnJavaContentViewCoreDestroyed(long nativeContentViewCoreImpl);
3079 private native void nativeLoadUrl(
3080 long nativeContentViewCoreImpl,
3086 int uaOverrideOption,
3087 String extraHeaders,
3089 String baseUrlForDataUrl,
3090 String virtualUrlForDataUrl,
3091 boolean canLoadLocalResources,
3092 boolean isRendererInitiated);
3094 private native void nativeSetFocus(long nativeContentViewCoreImpl, boolean focused);
3096 private native void nativeSendOrientationChangeEvent(
3097 long nativeContentViewCoreImpl, int orientation);
3099 // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
3100 private native boolean nativeOnTouchEvent(
3101 long nativeContentViewCoreImpl, MotionEvent event,
3102 long timeMs, int action, int pointerCount, int historySize, int actionIndex,
3103 float x0, float y0, float x1, float y1,
3104 int pointerId0, int pointerId1,
3105 float touchMajor0, float touchMajor1,
3106 float rawX, float rawY,
3107 int androidToolType0, int androidToolType1, int androidButtonState,
3108 boolean isTouchHandleEvent);
3110 private native int nativeSendMouseMoveEvent(
3111 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3113 private native int nativeSendMouseWheelEvent(
3114 long nativeContentViewCoreImpl, long timeMs, float x, float y, float verticalAxis);
3116 private native void nativeScrollBegin(
3117 long nativeContentViewCoreImpl, long timeMs, float x, float y, float hintX,
3120 private native void nativeScrollEnd(long nativeContentViewCoreImpl, long timeMs);
3122 private native void nativeScrollBy(
3123 long nativeContentViewCoreImpl, long timeMs, float x, float y,
3124 float deltaX, float deltaY);
3126 private native void nativeFlingStart(
3127 long nativeContentViewCoreImpl, long timeMs, float x, float y, float vx, float vy);
3129 private native void nativeFlingCancel(long nativeContentViewCoreImpl, long timeMs);
3131 private native void nativeSingleTap(
3132 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3134 private native void nativeDoubleTap(
3135 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3137 private native void nativeLongPress(
3138 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3140 private native void nativePinchBegin(
3141 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3143 private native void nativePinchEnd(long nativeContentViewCoreImpl, long timeMs);
3145 private native void nativePinchBy(long nativeContentViewCoreImpl, long timeMs,
3146 float anchorX, float anchorY, float deltaScale);
3148 private native void nativeSelectBetweenCoordinates(
3149 long nativeContentViewCoreImpl, float x1, float y1, float x2, float y2);
3151 private native void nativeMoveCaret(long nativeContentViewCoreImpl, float x, float y);
3153 private native void nativeHideTextHandles(long nativeContentViewCoreImpl);
3155 private native void nativeResetGestureDetection(long nativeContentViewCoreImpl);
3157 private native void nativeSetDoubleTapSupportEnabled(
3158 long nativeContentViewCoreImpl, boolean enabled);
3160 private native void nativeSetMultiTouchZoomSupportEnabled(
3161 long nativeContentViewCoreImpl, boolean enabled);
3163 private native void nativeSelectPopupMenuItems(long nativeContentViewCoreImpl, int[] indices);
3165 private native void nativeClearHistory(long nativeContentViewCoreImpl);
3167 private native void nativePostMessageToFrame(long nativeContentViewCoreImpl, String frameId,
3168 String message, String sourceOrigin, String targetOrigin);
3170 private native long nativeGetNativeImeAdapter(long nativeContentViewCoreImpl);
3172 private native int nativeGetCurrentRenderProcessId(long nativeContentViewCoreImpl);
3174 private native void nativeSetUseDesktopUserAgent(long nativeContentViewCoreImpl,
3175 boolean enabled, boolean reloadOnChange);
3177 private native boolean nativeGetUseDesktopUserAgent(long nativeContentViewCoreImpl);
3179 private native void nativeClearSslPreferences(long nativeContentViewCoreImpl);
3181 private native void nativeSetAllowJavascriptInterfacesInspection(
3182 long nativeContentViewCoreImpl, boolean allow);
3184 private native void nativeAddJavascriptInterface(long nativeContentViewCoreImpl, Object object,
3185 String name, Class requiredAnnotation);
3187 private native void nativeRemoveJavascriptInterface(long nativeContentViewCoreImpl,
3190 private native int nativeGetNavigationHistory(long nativeContentViewCoreImpl, Object context);
3192 private native void nativeGetDirectedNavigationHistory(long nativeContentViewCoreImpl,
3193 Object context, boolean isForward, int maxEntries);
3195 private native String nativeGetOriginalUrlForActiveNavigationEntry(
3196 long nativeContentViewCoreImpl);
3198 private native void nativeWasResized(long nativeContentViewCoreImpl);
3200 private native void nativeSetAccessibilityEnabled(
3201 long nativeContentViewCoreImpl, boolean enabled);
3203 private native void nativeExtractSmartClipData(long nativeContentViewCoreImpl,
3204 int x, int y, int w, int h);
3206 private native void nativeSetBackgroundOpaque(long nativeContentViewCoreImpl, boolean opaque);