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 org.chromium.base.ApiCompatibilityUtils;
51 import org.chromium.base.CalledByNative;
52 import org.chromium.base.CommandLine;
53 import org.chromium.base.JNINamespace;
54 import org.chromium.base.ObserverList;
55 import org.chromium.base.ObserverList.RewindableIterator;
56 import org.chromium.base.TraceEvent;
57 import org.chromium.base.VisibleForTesting;
58 import org.chromium.content.R;
59 import org.chromium.content.browser.ScreenOrientationListener.ScreenOrientationObserver;
60 import org.chromium.content.browser.accessibility.AccessibilityInjector;
61 import org.chromium.content.browser.accessibility.BrowserAccessibilityManager;
62 import org.chromium.content.browser.input.AdapterInputConnection;
63 import org.chromium.content.browser.input.GamepadList;
64 import org.chromium.content.browser.input.ImeAdapter;
65 import org.chromium.content.browser.input.ImeAdapter.AdapterInputConnectionFactory;
66 import org.chromium.content.browser.input.InputMethodManagerWrapper;
67 import org.chromium.content.browser.input.PastePopupMenu;
68 import org.chromium.content.browser.input.PastePopupMenu.PastePopupMenuDelegate;
69 import org.chromium.content.browser.input.PopupTouchHandleDrawable;
70 import org.chromium.content.browser.input.PopupTouchHandleDrawable.PopupTouchHandleDrawableDelegate;
71 import org.chromium.content.browser.input.SelectPopup;
72 import org.chromium.content.browser.input.SelectPopupDialog;
73 import org.chromium.content.browser.input.SelectPopupDropdown;
74 import org.chromium.content.browser.input.SelectPopupItem;
75 import org.chromium.content.browser.input.SelectionEventType;
76 import org.chromium.content.common.ContentSwitches;
77 import org.chromium.content_public.browser.GestureStateListener;
78 import org.chromium.content_public.browser.JavaScriptCallback;
79 import org.chromium.content_public.browser.WebContents;
80 import org.chromium.ui.base.DeviceFormFactor;
81 import org.chromium.ui.base.ViewAndroid;
82 import org.chromium.ui.base.ViewAndroidDelegate;
83 import org.chromium.ui.base.WindowAndroid;
84 import org.chromium.ui.gfx.DeviceDisplayInfo;
86 import java.lang.annotation.Annotation;
87 import java.lang.reflect.Field;
88 import java.util.ArrayList;
89 import java.util.HashMap;
90 import java.util.HashSet;
91 import java.util.List;
95 * Provides a Java-side 'wrapper' around a WebContent (native) instance.
96 * Contains all the major functionality necessary to manage the lifecycle of a ContentView without
97 * being tied to the view system.
99 @JNINamespace("content")
100 public class ContentViewCore
101 implements AccessibilityStateChangeListener, ScreenOrientationObserver {
103 private static final String TAG = "ContentViewCore";
105 // Used to avoid enabling zooming in / out if resulting zooming will
106 // produce little visible difference.
107 private static final float ZOOM_CONTROLS_EPSILON = 0.007f;
109 // Used to represent gestures for long press and long tap.
110 private static final int IS_LONG_PRESS = 1;
111 private static final int IS_LONG_TAP = 2;
113 private static final ZoomControlsDelegate NO_OP_ZOOM_CONTROLS_DELEGATE =
114 new ZoomControlsDelegate() {
116 public void invokeZoomPicker() {}
118 public void dismissZoomPicker() {}
120 public void updateZoomControls() {}
123 // If the embedder adds a JavaScript interface object that contains an indirect reference to
124 // the ContentViewCore, then storing a strong ref to the interface object on the native
125 // side would prevent garbage collection of the ContentViewCore (as that strong ref would
126 // create a new GC root).
127 // For that reason, we store only a weak reference to the interface object on the
128 // native side. However we still need a strong reference on the Java side to
129 // prevent garbage collection if the embedder doesn't maintain their own ref to the
130 // interface object - the Java side ref won't create a new GC root.
131 // This map stores those references. We put into the map on addJavaScriptInterface()
132 // and remove from it in removeJavaScriptInterface(). The annotation class is stored for
133 // the purpose of migrating injected objects from one instance of CVC to another, which
134 // is used by Android WebView to support WebChromeClient.onCreateWindow scenario.
135 private final Map<String, Pair<Object, Class>> mJavaScriptInterfaces =
136 new HashMap<String, Pair<Object, Class>>();
138 // Additionally, we keep track of all Java bound JS objects that are in use on the
139 // current page to ensure that they are not garbage collected until the page is
140 // navigated. This includes interface objects that have been removed
141 // via the removeJavaScriptInterface API and transient objects returned from methods
142 // on the interface object. Note we use HashSet rather than Set as the native side
143 // expects HashSet (no bindings for interfaces).
144 private final HashSet<Object> mRetainedJavaScriptObjects = new HashSet<Object>();
147 * Interface that consumers of {@link ContentViewCore} must implement to allow the proper
148 * dispatching of view methods through the containing view.
151 * All methods with the "super_" prefix should be routed to the parent of the
152 * implementing container view.
154 @SuppressWarnings("javadoc")
155 public interface InternalAccessDelegate {
157 * @see View#drawChild(Canvas, View, long)
159 boolean drawChild(Canvas canvas, View child, long drawingTime);
162 * @see View#onKeyUp(keyCode, KeyEvent)
164 boolean super_onKeyUp(int keyCode, KeyEvent event);
167 * @see View#dispatchKeyEventPreIme(KeyEvent)
169 boolean super_dispatchKeyEventPreIme(KeyEvent event);
172 * @see View#dispatchKeyEvent(KeyEvent)
174 boolean super_dispatchKeyEvent(KeyEvent event);
177 * @see View#onGenericMotionEvent(MotionEvent)
179 boolean super_onGenericMotionEvent(MotionEvent event);
182 * @see View#onConfigurationChanged(Configuration)
184 void super_onConfigurationChanged(Configuration newConfig);
187 * @see View#onScrollChanged(int, int, int, int)
189 void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix);
192 * @see View#awakenScrollBars()
194 boolean awakenScrollBars();
197 * @see View#awakenScrollBars(int, boolean)
199 boolean super_awakenScrollBars(int startDelay, boolean invalidate);
203 * An interface for controlling visibility and state of embedder-provided zoom controls.
205 public interface ZoomControlsDelegate {
207 * Called when it's reasonable to show zoom controls.
209 void invokeZoomPicker();
212 * Called when zoom controls need to be hidden (e.g. when the view hides).
214 void dismissZoomPicker();
217 * Called when page scale has been changed, so the controls can update their state.
219 void updateZoomControls();
223 * An interface that allows the embedder to be notified when the results of
224 * extractSmartClipData are available.
226 public interface SmartClipDataListener {
227 public void onSmartClipDataExtracted(String text, String html, Rect clipRect);
230 private final Context mContext;
231 private ViewGroup mContainerView;
232 private InternalAccessDelegate mContainerViewInternals;
233 private WebContents mWebContents;
234 private WebContentsObserverAndroid mWebContentsObserver;
236 private ContentViewClient mContentViewClient;
238 private ContentSettings mContentSettings;
240 // Native pointer to C++ ContentViewCoreImpl object which will be set by nativeInit().
241 private long mNativeContentViewCore = 0;
243 private final ObserverList<GestureStateListener> mGestureStateListeners;
244 private final RewindableIterator<GestureStateListener> mGestureStateListenersIterator;
245 private ZoomControlsDelegate mZoomControlsDelegate;
247 private PopupZoomer mPopupZoomer;
248 private SelectPopup mSelectPopup;
249 private long mNativeSelectPopupSourceFrame = 0;
251 private Runnable mFakeMouseMoveRunnable = null;
253 // Only valid when focused on a text / password field.
254 private ImeAdapter mImeAdapter;
255 private ImeAdapter.AdapterInputConnectionFactory mAdapterInputConnectionFactory;
256 private AdapterInputConnection mInputConnection;
257 private InputMethodManagerWrapper mInputMethodManagerWrapper;
259 // Lazily created paste popup menu, triggered either via long press in an
260 // editable region or from tapping the insertion handle.
261 private PastePopupMenu mPastePopupMenu;
262 private boolean mWasPastePopupShowingOnInsertionDragStart;
264 private PopupTouchHandleDrawableDelegate mTouchHandleDelegate;
266 private PositionObserver mPositionObserver;
268 // Size of the viewport in physical pixels as set from onSizeChanged.
269 private int mViewportWidthPix;
270 private int mViewportHeightPix;
271 private int mPhysicalBackingWidthPix;
272 private int mPhysicalBackingHeightPix;
273 private int mTopControlsLayoutHeightPix;
275 // Cached copy of all positions and scales as reported by the renderer.
276 private final RenderCoordinates mRenderCoordinates;
278 // Tracks whether a selection is currently active. When applied to selected text, indicates
279 // whether the last selected text is still highlighted.
280 private boolean mHasSelection;
281 private boolean mHasInsertion;
282 private String mLastSelectedText;
283 private boolean mFocusedNodeEditable;
284 private ActionMode mActionMode;
285 private boolean mUnselectAllOnActionModeDismiss;
286 private boolean mPreserveSelectionOnNextLossOfFocus;
288 // Delegate that will handle GET downloads, and be notified of completion of POST downloads.
289 private ContentViewDownloadDelegate mDownloadDelegate;
291 // The AccessibilityInjector that handles loading Accessibility scripts into the web page.
292 private AccessibilityInjector mAccessibilityInjector;
294 // Whether native accessibility, i.e. without any script injection, is allowed.
295 private boolean mNativeAccessibilityAllowed;
297 // Whether native accessibility, i.e. without any script injection, has been enabled.
298 private boolean mNativeAccessibilityEnabled;
300 // Handles native accessibility, i.e. without any script injection.
301 private BrowserAccessibilityManager mBrowserAccessibilityManager;
303 // System accessibility service.
304 private final AccessibilityManager mAccessibilityManager;
306 // Accessibility touch exploration state.
307 private boolean mTouchExplorationEnabled;
309 // Whether accessibility focus should be set to the page when it finishes loading.
310 // This only applies if an accessibility service like TalkBack is running.
311 // This is desirable behavior for a browser window, but not for an embedded
313 private boolean mShouldSetAccessibilityFocusOnPageLoad;
315 // Allows us to dynamically respond when the accessibility script injection flag changes.
316 private ContentObserver mAccessibilityScriptInjectionObserver;
318 // Temporary notification to tell onSizeChanged to focus a form element,
319 // because the OSK was just brought up.
320 private final Rect mFocusPreOSKViewportRect = new Rect();
322 // On tap this will store the x, y coordinates of the touch.
323 private int mLastTapX;
324 private int mLastTapY;
326 // Whether a touch scroll sequence is active, used to hide text selection
327 // handles. Note that a scroll sequence will *always* bound a pinch
328 // sequence, so this will also be true for the duration of a pinch gesture.
329 private boolean mTouchScrollInProgress;
331 // The outstanding fling start events that hasn't got fling end yet. It may be > 1 because
332 // onNativeFlingStopped() is called asynchronously.
333 private int mPotentiallyActiveFlingCount;
335 private ViewAndroid mViewAndroid;
337 private SmartClipDataListener mSmartClipDataListener = null;
339 // This holds the state of editable text (e.g. contents of <input>, contenteditable) of
340 // a focused element.
341 // Every time the user, IME, javascript (Blink), autofill etc. modifies the content, the new
342 // state must be reflected to this to keep consistency.
343 private final Editable mEditable;
346 * PID used to indicate an invalid render process.
348 // Keep in sync with the value returned from ContentViewCoreImpl::GetCurrentRendererProcessId()
349 // if there is no render process.
350 public static final int INVALID_RENDER_PROCESS_PID = 0;
352 // Offsets for the events that passes through this ContentViewCore.
353 private float mCurrentTouchOffsetX;
354 private float mCurrentTouchOffsetY;
356 // Offsets for smart clip
357 private int mSmartClipOffsetX;
358 private int mSmartClipOffsetY;
360 // Whether the ContentViewCore requires the WebContents to be fullscreen in order to lock the
361 // screen orientation.
362 private boolean mFullscreenRequiredForOrientationLock = true;
365 * Constructs a new ContentViewCore. Embedders must call initialize() after constructing
366 * a ContentViewCore and before using it.
368 * @param context The context used to create this.
370 public ContentViewCore(Context context) {
373 mAdapterInputConnectionFactory = new AdapterInputConnectionFactory();
374 mInputMethodManagerWrapper = new InputMethodManagerWrapper(mContext);
376 mRenderCoordinates = new RenderCoordinates();
377 float deviceScaleFactor = getContext().getResources().getDisplayMetrics().density;
378 String forceScaleFactor = CommandLine.getInstance().getSwitchValue(
379 ContentSwitches.FORCE_DEVICE_SCALE_FACTOR);
380 if (forceScaleFactor != null) {
381 deviceScaleFactor = Float.valueOf(forceScaleFactor);
383 mRenderCoordinates.setDeviceScaleFactor(deviceScaleFactor);
384 mAccessibilityManager = (AccessibilityManager)
385 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
386 mGestureStateListeners = new ObserverList<GestureStateListener>();
387 mGestureStateListenersIterator = mGestureStateListeners.rewindableIterator();
389 mEditable = Editable.Factory.getInstance().newEditable("");
390 Selection.setSelection(mEditable, 0);
394 * @return The context used for creating this ContentViewCore.
397 public Context getContext() {
402 * @return The ViewGroup that all view actions of this ContentViewCore should interact with.
404 public ViewGroup getContainerView() {
405 return mContainerView;
409 * @return The WebContents currently being rendered.
411 public WebContents getWebContents() {
415 /* TODO(aelias): Remove this after downstream callers switch to setTopControlsLayoutHeight. */
416 public void setViewportSizeOffset(int offsetXPix, int offsetYPix) {
417 setTopControlsLayoutHeight(offsetYPix);
421 * Specifies how much smaller the Blink layout size should be relative to the size of this
423 * @param topControlsLayoutHeightPix The Y amount in pixels to shrink the viewport by.
425 public void setTopControlsLayoutHeight(int topControlsLayoutHeightPix) {
426 if (topControlsLayoutHeightPix != mTopControlsLayoutHeightPix) {
427 mTopControlsLayoutHeightPix = topControlsLayoutHeightPix;
428 if (mNativeContentViewCore != 0) nativeWasResized(mNativeContentViewCore);
433 * Returns a delegate that can be used to add and remove views from the ContainerView.
435 * NOTE: Use with care, as not all ContentViewCore users setup their ContainerView in the same
436 * way. In particular, the Android WebView has limitations on what implementation details can
437 * be provided via a child view, as they are visible in the API and could introduce
438 * compatibility breaks with existing applications. If in doubt, contact the
439 * android_webview/OWNERS
441 * @return A ViewAndroidDelegate that can be used to add and remove views.
444 public ViewAndroidDelegate getViewAndroidDelegate() {
445 return new ViewAndroidDelegate() {
446 // mContainerView can change, but this ViewAndroidDelegate can only be used to
447 // add and remove views from the mContainerViewAtCreation.
448 private final ViewGroup mContainerViewAtCreation = mContainerView;
451 public View acquireAnchorView() {
452 View anchorView = new View(mContext);
453 mContainerViewAtCreation.addView(anchorView);
458 @SuppressWarnings("deprecation") // AbsoluteLayout
459 public void setAnchorViewPosition(
460 View view, float x, float y, float width, float height) {
461 if (view.getParent() == null) {
462 // Ignore. setAnchorViewPosition has been called after the anchor view has
463 // already been released.
466 assert view.getParent() == mContainerViewAtCreation;
468 float scale = (float) DeviceDisplayInfo.create(mContext).getDIPScale();
470 // The anchor view should not go outside the bounds of the ContainerView.
471 int leftMargin = Math.round(x * scale);
472 int topMargin = Math.round(mRenderCoordinates.getContentOffsetYPix() + y * scale);
473 int scaledWidth = Math.round(width * scale);
474 // ContentViewCore currently only supports these two container view types.
475 if (mContainerViewAtCreation instanceof FrameLayout) {
477 if (ApiCompatibilityUtils.isLayoutRtl(mContainerViewAtCreation)) {
478 startMargin = mContainerViewAtCreation.getMeasuredWidth()
479 - Math.round((width + x) * scale);
481 startMargin = leftMargin;
483 if (scaledWidth + startMargin > mContainerViewAtCreation.getWidth()) {
484 scaledWidth = mContainerViewAtCreation.getWidth() - startMargin;
486 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
487 scaledWidth, Math.round(height * scale));
488 ApiCompatibilityUtils.setMarginStart(lp, startMargin);
489 lp.topMargin = topMargin;
490 view.setLayoutParams(lp);
491 } else if (mContainerViewAtCreation instanceof android.widget.AbsoluteLayout) {
492 // This fixes the offset due to a difference in
493 // scrolling model of WebView vs. Chrome.
494 // TODO(sgurun) fix this to use mContainerViewAtCreation.getScroll[X/Y]()
495 // as it naturally accounts for scroll differences between
497 leftMargin += mRenderCoordinates.getScrollXPixInt();
498 topMargin += mRenderCoordinates.getScrollYPixInt();
500 android.widget.AbsoluteLayout.LayoutParams lp =
501 new android.widget.AbsoluteLayout.LayoutParams(
502 scaledWidth, (int) (height * scale), leftMargin, topMargin);
503 view.setLayoutParams(lp);
505 Log.e(TAG, "Unknown layout " + mContainerViewAtCreation.getClass().getName());
510 public void releaseAnchorView(View anchorView) {
511 mContainerViewAtCreation.removeView(anchorView);
517 public void setImeAdapterForTest(ImeAdapter imeAdapter) {
518 mImeAdapter = imeAdapter;
522 public ImeAdapter getImeAdapterForTest() {
527 public void setAdapterInputConnectionFactory(AdapterInputConnectionFactory factory) {
528 mAdapterInputConnectionFactory = factory;
532 public void setInputMethodManagerWrapperForTest(InputMethodManagerWrapper immw) {
533 mInputMethodManagerWrapper = immw;
537 public AdapterInputConnection getInputConnectionForTest() {
538 return mInputConnection;
541 private ImeAdapter createImeAdapter(Context context) {
542 return new ImeAdapter(mInputMethodManagerWrapper,
543 new ImeAdapter.ImeAdapterDelegate() {
545 public void onImeEvent() {
546 mPopupZoomer.hide(true);
547 getContentViewClient().onImeEvent();
548 if (mFocusedNodeEditable) hideTextHandles();
552 public void onDismissInput() {
553 getContentViewClient().onImeStateChangeRequested(false);
557 public View getAttachedView() {
558 return mContainerView;
562 public ResultReceiver getNewShowKeyboardReceiver() {
563 return new ResultReceiver(new Handler()) {
565 public void onReceiveResult(int resultCode, Bundle resultData) {
566 getContentViewClient().onImeStateChangeRequested(
567 resultCode == InputMethodManager.RESULT_SHOWN ||
568 resultCode == InputMethodManager.RESULT_UNCHANGED_SHOWN);
569 if (resultCode == InputMethodManager.RESULT_SHOWN) {
570 // If OSK is newly shown, delay the form focus until
571 // the onSizeChanged (in order to adjust relative to the
573 // TODO(jdduke): We should not assume that onSizeChanged will
574 // always be called, crbug.com/294908.
575 getContainerView().getWindowVisibleDisplayFrame(
576 mFocusPreOSKViewportRect);
577 } else if (hasFocus() && resultCode ==
578 InputMethodManager.RESULT_UNCHANGED_SHOWN) {
579 // If the OSK was already there, focus the form immediately.
580 scrollFocusedEditableNodeIntoView();
591 * @param containerView The view that will act as a container for all views created by this.
592 * @param internalDispatcher Handles dispatching all hidden or super methods to the
594 * @param nativeWebContents A pointer to the native web contents.
595 * @param windowAndroid An instance of the WindowAndroid.
597 // Perform important post-construction set up of the ContentViewCore.
598 // We do not require the containing view in the constructor to allow embedders to create a
599 // ContentViewCore without having fully created its containing view. The containing view
600 // is a vital component of the ContentViewCore, so embedders must exercise caution in what
601 // they do with the ContentViewCore before calling initialize().
602 // We supply the nativeWebContents pointer here rather than in the constructor to allow us
603 // to set the private browsing mode at a later point for the WebView implementation.
604 // Note that the caller remains the owner of the nativeWebContents and is responsible for
605 // deleting it after destroying the ContentViewCore.
606 public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
607 long nativeWebContents, WindowAndroid windowAndroid) {
608 setContainerView(containerView);
610 long windowNativePointer = windowAndroid.getNativePointer();
611 assert windowNativePointer != 0;
612 mViewAndroid = new ViewAndroid(windowAndroid, getViewAndroidDelegate());
613 long viewAndroidNativePointer = mViewAndroid.getNativePointer();
614 assert viewAndroidNativePointer != 0;
616 mZoomControlsDelegate = NO_OP_ZOOM_CONTROLS_DELEGATE;
618 mNativeContentViewCore = nativeInit(
619 nativeWebContents, viewAndroidNativePointer, windowNativePointer,
620 mRetainedJavaScriptObjects);
621 mWebContents = nativeGetWebContentsAndroid(mNativeContentViewCore);
622 mContentSettings = new ContentSettings(this, mNativeContentViewCore);
624 setContainerViewInternals(internalDispatcher);
625 mRenderCoordinates.reset();
626 initPopupZoomer(mContext);
627 mImeAdapter = createImeAdapter(mContext);
629 mAccessibilityInjector = AccessibilityInjector.newInstance(this);
631 mWebContentsObserver = new WebContentsObserverAndroid(mWebContents) {
633 public void didNavigateMainFrame(String url, String baseUrl,
634 boolean isNavigationToDifferentPage, boolean isFragmentNavigation) {
635 if (!isNavigationToDifferentPage) return;
636 hidePopupsAndClearSelection();
637 resetScrollInProgress();
638 resetGestureDetection();
642 public void renderProcessGone(boolean wasOomProtected) {
643 hidePopupsAndClearSelection();
644 resetScrollInProgress();
645 // No need to reset gesture detection as the detector will have
646 // been destroyed in the RenderWidgetHostView.
652 * Sets a new container view for this {@link ContentViewCore}.
654 * <p>WARNING: This is not a general purpose method and has been designed with WebView
655 * fullscreen in mind. Please be aware that it might not be appropriate for other use cases
656 * and that it has a number of limitations. For example the PopupZoomer only works with the
657 * container view with which this ContentViewCore has been initialized.
659 * <p>This method only performs a small part of replacing the container view and
660 * embedders are responsible for:
662 * <li>Disconnecting the old container view from this ContentViewCore</li>
663 * <li>Updating the InternalAccessDelegate</li>
664 * <li>Reconciling the state of this ContentViewCore with the new container view</li>
665 * <li>Tearing down and recreating the native GL rendering where appropriate</li>
669 public void setContainerView(ViewGroup containerView) {
671 if (mContainerView != null) {
672 mPastePopupMenu = null;
673 mInputConnection = null;
674 hidePopupsAndClearSelection();
677 mContainerView = containerView;
678 mPositionObserver = new ViewPositionObserver(mContainerView);
679 String contentDescription = "Web View";
680 if (R.string.accessibility_content_view == 0) {
681 Log.w(TAG, "Setting contentDescription to 'Web View' as no value was specified.");
683 contentDescription = mContext.getResources().getString(
684 R.string.accessibility_content_view);
686 mContainerView.setContentDescription(contentDescription);
687 mContainerView.setWillNotDraw(false);
688 mContainerView.setClickable(true);
693 void onNativeContentViewCoreDestroyed(long nativeContentViewCore) {
694 assert nativeContentViewCore == mNativeContentViewCore;
695 mNativeContentViewCore = 0;
699 * Set the Container view Internals.
700 * @param internalDispatcher Handles dispatching all hidden or super methods to the
703 public void setContainerViewInternals(InternalAccessDelegate internalDispatcher) {
704 mContainerViewInternals = internalDispatcher;
707 private void initPopupZoomer(Context context) {
708 mPopupZoomer = new PopupZoomer(context);
709 mPopupZoomer.setOnVisibilityChangedListener(new PopupZoomer.OnVisibilityChangedListener() {
710 // mContainerView can change, but this OnVisibilityChangedListener can only be used
711 // to add and remove views from the mContainerViewAtCreation.
712 private final ViewGroup mContainerViewAtCreation = mContainerView;
715 public void onPopupZoomerShown(final PopupZoomer zoomer) {
716 mContainerViewAtCreation.post(new Runnable() {
719 if (mContainerViewAtCreation.indexOfChild(zoomer) == -1) {
720 mContainerViewAtCreation.addView(zoomer);
722 assert false : "PopupZoomer should never be shown without being hidden";
729 public void onPopupZoomerHidden(final PopupZoomer zoomer) {
730 mContainerViewAtCreation.post(new Runnable() {
733 if (mContainerViewAtCreation.indexOfChild(zoomer) != -1) {
734 mContainerViewAtCreation.removeView(zoomer);
735 mContainerViewAtCreation.invalidate();
737 assert false : "PopupZoomer should never be hidden without being shown";
743 // TODO(yongsheng): LONG_TAP is not enabled in PopupZoomer. So need to dispatch a LONG_TAP
744 // gesture if a user completes a tap on PopupZoomer UI after a LONG_PRESS gesture.
745 PopupZoomer.OnTapListener listener = new PopupZoomer.OnTapListener() {
746 // mContainerView can change, but this OnTapListener can only be used
747 // with the mContainerViewAtCreation.
748 private final ViewGroup mContainerViewAtCreation = mContainerView;
751 public boolean onSingleTap(View v, MotionEvent e) {
752 mContainerViewAtCreation.requestFocus();
753 if (mNativeContentViewCore != 0) {
754 nativeSingleTap(mNativeContentViewCore, e.getEventTime(), e.getX(), e.getY());
760 public boolean onLongPress(View v, MotionEvent e) {
761 if (mNativeContentViewCore != 0) {
762 nativeLongPress(mNativeContentViewCore, e.getEventTime(), e.getX(), e.getY());
767 mPopupZoomer.setOnTapListener(listener);
771 public void setPopupZoomerForTest(PopupZoomer popupZoomer) {
772 mPopupZoomer = popupZoomer;
776 * Destroy the internal state of the ContentView. This method may only be
777 * called after the ContentView has been removed from the view system. No
778 * other methods may be called on this ContentView after this method has
781 public void destroy() {
782 if (mNativeContentViewCore != 0) {
783 nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore);
785 mWebContentsObserver.detachFromWebContents();
786 mWebContentsObserver = null;
787 setSmartClipDataListener(null);
788 setZoomControlsDelegate(null);
789 // TODO(igsolla): address TODO in ContentViewClient because ContentViewClient is not
790 // currently a real Null Object.
792 // Instead of deleting the client we use the Null Object pattern to avoid null checks
794 mContentViewClient = new ContentViewClient();
796 if (mViewAndroid != null) mViewAndroid.destroy();
797 mNativeContentViewCore = 0;
798 mContentSettings = null;
799 mJavaScriptInterfaces.clear();
800 mRetainedJavaScriptObjects.clear();
801 unregisterAccessibilityContentObserver();
802 mGestureStateListeners.clear();
803 ScreenOrientationListener.getInstance().removeObserver(this);
804 mPositionObserver.clearListener();
807 private void unregisterAccessibilityContentObserver() {
808 if (mAccessibilityScriptInjectionObserver == null) {
811 getContext().getContentResolver().unregisterContentObserver(
812 mAccessibilityScriptInjectionObserver);
813 mAccessibilityScriptInjectionObserver = null;
817 * Returns true initially, false after destroy() has been called.
818 * It is illegal to call any other public method after destroy().
820 public boolean isAlive() {
821 return mNativeContentViewCore != 0;
825 * This is only useful for passing over JNI to native code that requires ContentViewCore*.
826 * @return native ContentViewCore pointer.
829 public long getNativeContentViewCore() {
830 return mNativeContentViewCore;
833 public void setContentViewClient(ContentViewClient client) {
834 if (client == null) {
835 throw new IllegalArgumentException("The client can't be null.");
837 mContentViewClient = client;
841 public ContentViewClient getContentViewClient() {
842 if (mContentViewClient == null) {
843 // We use the Null Object pattern to avoid having to perform a null check in this class.
844 // We create it lazily because most of the time a client will be set almost immediately
845 // after ContentView is created.
846 mContentViewClient = new ContentViewClient();
847 // We don't set the native ContentViewClient pointer here on purpose. The native
848 // implementation doesn't mind a null delegate and using one is better than passing a
849 // Null Object, since we cut down on the number of JNI calls.
851 return mContentViewClient;
855 private void onBackgroundColorChanged(int color) {
856 getContentViewClient().onBackgroundColorChanged(color);
860 * Shows an interstitial page driven by the passed in delegate.
862 * @param url The URL being blocked by the interstitial.
863 * @param delegate The delegate handling the interstitial.
866 public void showInterstitialPage(
867 String url, InterstitialPageDelegateAndroid delegate) {
868 assert mWebContents != null;
869 mWebContents.showInterstitialPage(url, delegate.getNative());
873 * @return Whether the page is currently showing an interstitial, such as a bad HTTPS page.
875 public boolean isShowingInterstitialPage() {
876 assert mWebContents != null;
877 return mWebContents.isShowingInterstitialPage();
881 * @return Viewport width in physical pixels as set from onSizeChanged.
884 public int getViewportWidthPix() { return mViewportWidthPix; }
887 * @return Viewport height in physical pixels as set from onSizeChanged.
890 public int getViewportHeightPix() { return mViewportHeightPix; }
893 * @return Width of underlying physical surface.
896 public int getPhysicalBackingWidthPix() { return mPhysicalBackingWidthPix; }
899 * @return Height of underlying physical surface.
902 public int getPhysicalBackingHeightPix() { return mPhysicalBackingHeightPix; }
904 /* TODO(aelias): Remove these when downstream callers disappear. */
906 public int getViewportSizeOffsetWidthPix() { return 0; }
908 public int getViewportSizeOffsetHeightPix() { return getTopControlsLayoutHeightPix(); }
911 * @return The amount that the viewport size given to Blink is shrunk by the URL-bar..
914 public int getTopControlsLayoutHeightPix() { return mTopControlsLayoutHeightPix; }
917 * @see android.webkit.WebView#getContentHeight()
919 public float getContentHeightCss() {
920 return mRenderCoordinates.getContentHeightCss();
924 * @see android.webkit.WebView#getContentWidth()
926 public float getContentWidthCss() {
927 return mRenderCoordinates.getContentWidthCss();
931 * @return The selected text (empty if no text selected).
933 public String getSelectedText() {
934 return mHasSelection ? mLastSelectedText : "";
938 * @return Whether the current selection is editable (false if no text selected).
940 public boolean isSelectionEditable() {
941 return mHasSelection ? mFocusedNodeEditable : false;
945 * @return Whether the current focused node is editable.
947 public boolean isFocusedNodeEditable() {
948 return mFocusedNodeEditable;
951 // End FrameLayout overrides.
954 * @see View#onTouchEvent(MotionEvent)
956 public boolean onTouchEvent(MotionEvent event) {
957 final boolean isTouchHandleEvent = false;
958 return onTouchEventImpl(event, isTouchHandleEvent);
961 private boolean onTouchEventImpl(MotionEvent event, boolean isTouchHandleEvent) {
962 TraceEvent.begin("onTouchEvent");
964 int eventAction = event.getActionMasked();
966 if (eventAction == MotionEvent.ACTION_DOWN) {
967 cancelRequestToScrollFocusedEditableNodeIntoView();
970 if (SPenSupport.isSPenSupported(mContext))
971 eventAction = SPenSupport.convertSPenEventAction(eventAction);
972 if (!isValidTouchEventActionForNative(eventAction)) return false;
974 if (mNativeContentViewCore == 0) return false;
976 // A zero offset is quite common, in which case the unnecessary copy should be avoided.
977 MotionEvent offset = null;
978 if (mCurrentTouchOffsetX != 0 || mCurrentTouchOffsetY != 0) {
979 offset = createOffsetMotionEvent(event);
983 final int pointerCount = event.getPointerCount();
984 final boolean consumed = nativeOnTouchEvent(mNativeContentViewCore, event,
985 event.getEventTime(), eventAction,
986 pointerCount, event.getHistorySize(), event.getActionIndex(),
987 event.getX(), event.getY(),
988 pointerCount > 1 ? event.getX(1) : 0,
989 pointerCount > 1 ? event.getY(1) : 0,
990 event.getPointerId(0), pointerCount > 1 ? event.getPointerId(1) : -1,
991 event.getTouchMajor(), pointerCount > 1 ? event.getTouchMajor(1) : 0,
992 event.getTouchMinor(), pointerCount > 1 ? event.getTouchMinor(1) : 0,
993 event.getOrientation(), pointerCount > 1 ? event.getOrientation(1) : 0,
994 event.getRawX(), event.getRawY(),
995 event.getToolType(0),
996 pointerCount > 1 ? event.getToolType(1) : MotionEvent.TOOL_TYPE_UNKNOWN,
997 event.getButtonState(),
998 event.getMetaState(),
1001 if (offset != null) offset.recycle();
1004 TraceEvent.end("onTouchEvent");
1008 private static boolean isValidTouchEventActionForNative(int eventAction) {
1009 // Only these actions have any effect on gesture detection. Other
1010 // actions have no corresponding WebTouchEvent type and may confuse the
1011 // touch pipline, so we ignore them entirely.
1012 return eventAction == MotionEvent.ACTION_DOWN
1013 || eventAction == MotionEvent.ACTION_UP
1014 || eventAction == MotionEvent.ACTION_CANCEL
1015 || eventAction == MotionEvent.ACTION_MOVE
1016 || eventAction == MotionEvent.ACTION_POINTER_DOWN
1017 || eventAction == MotionEvent.ACTION_POINTER_UP;
1020 public void setIgnoreRemainingTouchEvents() {
1021 resetGestureDetection();
1024 public boolean isScrollInProgress() {
1025 return mTouchScrollInProgress || mPotentiallyActiveFlingCount > 0;
1028 @SuppressWarnings("unused")
1030 private void onFlingStartEventConsumed(int vx, int vy) {
1031 mTouchScrollInProgress = false;
1032 mPotentiallyActiveFlingCount++;
1033 for (mGestureStateListenersIterator.rewind();
1034 mGestureStateListenersIterator.hasNext();) {
1035 mGestureStateListenersIterator.next().onFlingStartGesture(
1036 vx, vy, computeVerticalScrollOffset(), computeVerticalScrollExtent());
1040 @SuppressWarnings("unused")
1042 private void onFlingStartEventHadNoConsumer(int vx, int vy) {
1043 mTouchScrollInProgress = false;
1044 for (mGestureStateListenersIterator.rewind();
1045 mGestureStateListenersIterator.hasNext();) {
1046 mGestureStateListenersIterator.next().onUnhandledFlingStartEvent(vx, vy);
1050 @SuppressWarnings("unused")
1052 private void onFlingCancelEventAck() {
1053 updateGestureStateListener(GestureEventType.FLING_CANCEL);
1056 @SuppressWarnings("unused")
1058 private void onScrollBeginEventAck() {
1059 mTouchScrollInProgress = true;
1061 mZoomControlsDelegate.invokeZoomPicker();
1062 updateGestureStateListener(GestureEventType.SCROLL_START);
1065 @SuppressWarnings("unused")
1067 private void onScrollUpdateGestureConsumed() {
1068 mZoomControlsDelegate.invokeZoomPicker();
1069 for (mGestureStateListenersIterator.rewind();
1070 mGestureStateListenersIterator.hasNext();) {
1071 mGestureStateListenersIterator.next().onScrollUpdateGestureConsumed();
1075 @SuppressWarnings("unused")
1077 private void onScrollEndEventAck() {
1078 if (!mTouchScrollInProgress) return;
1079 mTouchScrollInProgress = false;
1080 updateGestureStateListener(GestureEventType.SCROLL_END);
1083 @SuppressWarnings("unused")
1085 private void onPinchBeginEventAck() {
1086 updateGestureStateListener(GestureEventType.PINCH_BEGIN);
1089 @SuppressWarnings("unused")
1091 private void onPinchEndEventAck() {
1092 updateGestureStateListener(GestureEventType.PINCH_END);
1095 @SuppressWarnings("unused")
1097 private void onSingleTapEventAck(boolean consumed, int x, int y) {
1098 for (mGestureStateListenersIterator.rewind();
1099 mGestureStateListenersIterator.hasNext();) {
1100 mGestureStateListenersIterator.next().onSingleTap(consumed, x, y);
1105 * Called just prior to a tap or press gesture being forwarded to the renderer.
1107 @SuppressWarnings("unused")
1109 private boolean filterTapOrPressEvent(int type, int x, int y) {
1110 if (type == GestureEventType.LONG_PRESS && offerLongPressToEmbedder()) {
1113 updateForTapOrPress(type, x, y);
1118 public void sendDoubleTapForTest(long timeMs, int x, int y) {
1119 if (mNativeContentViewCore == 0) return;
1120 nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1124 public void flingForTest(long timeMs, int x, int y, int velocityX, int velocityY) {
1125 if (mNativeContentViewCore == 0) return;
1126 nativeFlingCancel(mNativeContentViewCore, timeMs);
1127 nativeScrollBegin(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1128 nativeFlingStart(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1132 * Cancel any fling gestures active.
1133 * @param timeMs Current time (in milliseconds).
1135 public void cancelFling(long timeMs) {
1136 if (mNativeContentViewCore == 0) return;
1137 nativeFlingCancel(mNativeContentViewCore, timeMs);
1141 * Add a listener that gets alerted on gesture state changes.
1142 * @param listener Listener to add.
1144 public void addGestureStateListener(GestureStateListener listener) {
1145 mGestureStateListeners.addObserver(listener);
1149 * Removes a listener that was added to watch for gesture state changes.
1150 * @param listener Listener to remove.
1152 public void removeGestureStateListener(GestureStateListener listener) {
1153 mGestureStateListeners.removeObserver(listener);
1156 void updateGestureStateListener(int gestureType) {
1157 for (mGestureStateListenersIterator.rewind();
1158 mGestureStateListenersIterator.hasNext();) {
1159 GestureStateListener listener = mGestureStateListenersIterator.next();
1160 switch (gestureType) {
1161 case GestureEventType.PINCH_BEGIN:
1162 listener.onPinchStarted();
1164 case GestureEventType.PINCH_END:
1165 listener.onPinchEnded();
1167 case GestureEventType.FLING_END:
1168 listener.onFlingEndGesture(
1169 computeVerticalScrollOffset(),
1170 computeVerticalScrollExtent());
1172 case GestureEventType.FLING_CANCEL:
1173 listener.onFlingCancelGesture();
1175 case GestureEventType.SCROLL_START:
1176 listener.onScrollStarted(
1177 computeVerticalScrollOffset(),
1178 computeVerticalScrollExtent());
1180 case GestureEventType.SCROLL_END:
1181 listener.onScrollEnded(
1182 computeVerticalScrollOffset(),
1183 computeVerticalScrollExtent());
1192 * Inserts the provided markup sandboxed into the frame.
1194 public void setupTransitionView(String markup) {
1195 assert mWebContents != null;
1196 mWebContents.setupTransitionView(markup);
1200 * Hides transition elements specified by the selector, and activates any
1201 * exiting-transition stylesheets.
1203 public void beginExitTransition(String cssSelector) {
1204 assert mWebContents != null;
1205 mWebContents.beginExitTransition(cssSelector);
1209 * Requests the renderer insert a link to the specified stylesheet in the
1210 * main frame's document.
1212 public void addStyleSheetByURL(String url) {
1213 assert mWebContents != null;
1214 mWebContents.addStyleSheetByURL(url);
1218 * Injects the passed Javascript code in the current page and evaluates it.
1219 * If a result is required, pass in a callback.
1220 * Used in automation tests.
1222 * @param script The Javascript to execute.
1223 * @param callback The callback to be fired off when a result is ready. The script's
1224 * result will be json encoded and passed as the parameter, and the call
1225 * will be made on the main thread.
1226 * If no result is required, pass null.
1228 public void evaluateJavaScript(String script, JavaScriptCallback callback) {
1229 assert mWebContents != null;
1230 mWebContents.evaluateJavaScript(script, callback);
1234 * Post a message to a frame.
1235 * TODO(sgurun) also add support for transferring a message channel port.
1237 * @param frameName The name of the frame. If the name is null the message is posted
1238 * to the main frame.
1239 * @param message The message
1240 * @param sourceOrigin The source origin
1241 * @param targetOrigin The target origin
1243 public void postMessageToFrame(String frameName, String message,
1244 String sourceOrigin, String targetOrigin) {
1245 if (mNativeContentViewCore == 0) return;
1246 nativePostMessageToFrame(mNativeContentViewCore, frameName, message, sourceOrigin,
1251 * To be called when the ContentView is shown.
1253 public void onShow() {
1254 assert mWebContents != null;
1255 mWebContents.onShow();
1256 setAccessibilityState(mAccessibilityManager.isEnabled());
1257 restoreSelectionPopupsIfNecessary();
1261 * @return The ID of the renderer process that backs this tab or
1262 * {@link #INVALID_RENDER_PROCESS_PID} if there is none.
1265 public int getCurrentRenderProcessId() {
1266 return nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1270 * To be called when the ContentView is hidden.
1272 public void onHide() {
1273 assert mWebContents != null;
1274 hidePopupsAndPreserveSelection();
1275 setInjectedAccessibility(false);
1276 mWebContents.onHide();
1280 * Return the ContentSettings object used to retrieve the settings for this
1281 * ContentViewCore. For modifications, ChromeNativePreferences is to be used.
1282 * @return A ContentSettings object that can be used to retrieve this
1283 * ContentViewCore's settings.
1285 public ContentSettings getContentSettings() {
1286 return mContentSettings;
1289 private void hidePopupsAndClearSelection() {
1290 mUnselectAllOnActionModeDismiss = true;
1294 private void hidePopupsAndPreserveSelection() {
1295 mUnselectAllOnActionModeDismiss = false;
1299 private void hidePopups() {
1300 hideSelectActionBar();
1303 mPopupZoomer.hide(false);
1304 if (mUnselectAllOnActionModeDismiss) hideTextHandles();
1307 private void restoreSelectionPopupsIfNecessary() {
1308 if (mHasSelection && mActionMode == null) showSelectActionBar();
1311 public void hideSelectActionBar() {
1312 if (mActionMode != null) {
1313 mActionMode.finish();
1318 public boolean isSelectActionBarShowing() {
1319 return mActionMode != null;
1322 private void resetGestureDetection() {
1323 if (mNativeContentViewCore == 0) return;
1324 nativeResetGestureDetection(mNativeContentViewCore);
1328 * @see View#onAttachedToWindow()
1330 @SuppressWarnings("javadoc")
1331 public void onAttachedToWindow() {
1332 setAccessibilityState(mAccessibilityManager.isEnabled());
1333 restoreSelectionPopupsIfNecessary();
1334 ScreenOrientationListener.getInstance().addObserver(this, mContext);
1335 GamepadList.onAttachedToWindow(mContext);
1339 * @see View#onDetachedFromWindow()
1341 @SuppressWarnings("javadoc")
1342 @SuppressLint("MissingSuperCall")
1343 public void onDetachedFromWindow() {
1344 setInjectedAccessibility(false);
1345 hidePopupsAndPreserveSelection();
1346 mZoomControlsDelegate.dismissZoomPicker();
1347 unregisterAccessibilityContentObserver();
1349 ScreenOrientationListener.getInstance().removeObserver(this);
1350 GamepadList.onDetachedFromWindow();
1354 * @see View#onVisibilityChanged(android.view.View, int)
1356 public void onVisibilityChanged(View changedView, int visibility) {
1357 if (visibility != View.VISIBLE) {
1358 mZoomControlsDelegate.dismissZoomPicker();
1363 * @see View#onCreateInputConnection(EditorInfo)
1365 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1366 if (!mImeAdapter.hasTextInputType()) {
1367 // Although onCheckIsTextEditor will return false in this case, the EditorInfo
1368 // is still used by the InputMethodService. Need to make sure the IME doesn't
1369 // enter fullscreen mode.
1370 outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
1372 mInputConnection = mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter,
1373 mEditable, outAttrs);
1374 return mInputConnection;
1378 public AdapterInputConnection getAdapterInputConnectionForTest() {
1379 return mInputConnection;
1383 public Editable getEditableForTest() {
1388 * @see View#onCheckIsTextEditor()
1390 public boolean onCheckIsTextEditor() {
1391 return mImeAdapter.hasTextInputType();
1395 * @see View#onConfigurationChanged(Configuration)
1397 @SuppressWarnings("javadoc")
1398 public void onConfigurationChanged(Configuration newConfig) {
1401 if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
1402 if (mNativeContentViewCore != 0) {
1403 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
1404 ImeAdapter.getTextInputTypeNone(), 0 /* no flags */);
1406 mInputMethodManagerWrapper.restartInput(mContainerView);
1408 mContainerViewInternals.super_onConfigurationChanged(newConfig);
1410 // To request layout has side effect, but it seems OK as it only happen in
1411 // onConfigurationChange and layout has to be changed in most case.
1412 mContainerView.requestLayout();
1417 * @see View#onSizeChanged(int, int, int, int)
1419 @SuppressWarnings("javadoc")
1420 public void onSizeChanged(int wPix, int hPix, int owPix, int ohPix) {
1421 if (getViewportWidthPix() == wPix && getViewportHeightPix() == hPix) return;
1423 mViewportWidthPix = wPix;
1424 mViewportHeightPix = hPix;
1425 if (mNativeContentViewCore != 0) {
1426 nativeWasResized(mNativeContentViewCore);
1429 updateAfterSizeChanged();
1433 * Called when the underlying surface the compositor draws to changes size.
1434 * This may be larger than the viewport size.
1436 public void onPhysicalBackingSizeChanged(int wPix, int hPix) {
1437 if (mPhysicalBackingWidthPix == wPix && mPhysicalBackingHeightPix == hPix) return;
1439 mPhysicalBackingWidthPix = wPix;
1440 mPhysicalBackingHeightPix = hPix;
1442 if (mNativeContentViewCore != 0) {
1443 nativeWasResized(mNativeContentViewCore);
1447 /* TODO(aelias): Remove this after downstream callers disappear. */
1448 public void onOverdrawBottomHeightChanged(int overdrawHeightPix) {
1451 private void updateAfterSizeChanged() {
1452 mPopupZoomer.hide(false);
1454 // Execute a delayed form focus operation because the OSK was brought
1456 if (!mFocusPreOSKViewportRect.isEmpty()) {
1457 Rect rect = new Rect();
1458 getContainerView().getWindowVisibleDisplayFrame(rect);
1459 if (!rect.equals(mFocusPreOSKViewportRect)) {
1460 // Only assume the OSK triggered the onSizeChanged if width was preserved.
1461 if (rect.width() == mFocusPreOSKViewportRect.width()) {
1462 scrollFocusedEditableNodeIntoView();
1464 cancelRequestToScrollFocusedEditableNodeIntoView();
1469 private void cancelRequestToScrollFocusedEditableNodeIntoView() {
1470 // Zero-ing the rect will prevent |updateAfterSizeChanged()| from
1471 // issuing the delayed form focus event.
1472 mFocusPreOSKViewportRect.setEmpty();
1475 private void scrollFocusedEditableNodeIntoView() {
1476 assert mWebContents != null;
1477 mWebContents.scrollFocusedEditableNodeIntoView();
1481 * Selects the word around the caret, if any.
1482 * The caller can check if selection actually occurred by listening to OnSelectionChanged.
1484 public void selectWordAroundCaret() {
1485 assert mWebContents != null;
1486 mWebContents.selectWordAroundCaret();
1490 * @see View#onWindowFocusChanged(boolean)
1492 public void onWindowFocusChanged(boolean hasWindowFocus) {
1493 if (!hasWindowFocus) resetGestureDetection();
1496 public void onFocusChanged(boolean gainFocus) {
1498 restoreSelectionPopupsIfNecessary();
1501 cancelRequestToScrollFocusedEditableNodeIntoView();
1502 if (mPreserveSelectionOnNextLossOfFocus) {
1503 mPreserveSelectionOnNextLossOfFocus = false;
1504 hidePopupsAndPreserveSelection();
1506 hidePopupsAndClearSelection();
1509 if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1513 * @see View#onKeyUp(int, KeyEvent)
1515 public boolean onKeyUp(int keyCode, KeyEvent event) {
1516 if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1517 mPopupZoomer.hide(true);
1520 return mContainerViewInternals.super_onKeyUp(keyCode, event);
1524 * @see View#dispatchKeyEventPreIme(KeyEvent)
1526 public boolean dispatchKeyEventPreIme(KeyEvent event) {
1529 return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1536 * @see View#dispatchKeyEvent(KeyEvent)
1538 public boolean dispatchKeyEvent(KeyEvent event) {
1539 if (GamepadList.dispatchKeyEvent(event)) return true;
1540 if (getContentViewClient().shouldOverrideKeyEvent(event)) {
1541 return mContainerViewInternals.super_dispatchKeyEvent(event);
1544 if (mImeAdapter.dispatchKeyEvent(event)) return true;
1546 return mContainerViewInternals.super_dispatchKeyEvent(event);
1550 * @see View#onHoverEvent(MotionEvent)
1551 * Mouse move events are sent on hover enter, hover move and hover exit.
1552 * They are sent on hover exit because sometimes it acts as both a hover
1553 * move and hover exit.
1555 public boolean onHoverEvent(MotionEvent event) {
1556 TraceEvent.begin("onHoverEvent");
1557 MotionEvent offset = createOffsetMotionEvent(event);
1559 if (mBrowserAccessibilityManager != null) {
1560 return mBrowserAccessibilityManager.onHoverEvent(offset);
1563 // Work around Android bug where the x, y coordinates of a hover exit
1564 // event are incorrect when touch exploration is on.
1565 if (mTouchExplorationEnabled && offset.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
1569 mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1570 if (mNativeContentViewCore != 0) {
1571 nativeSendMouseMoveEvent(mNativeContentViewCore, offset.getEventTime(),
1572 offset.getX(), offset.getY());
1577 TraceEvent.end("onHoverEvent");
1582 * @see View#onGenericMotionEvent(MotionEvent)
1584 public boolean onGenericMotionEvent(MotionEvent event) {
1585 if (GamepadList.onGenericMotionEvent(event)) return true;
1586 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1587 switch (event.getAction()) {
1588 case MotionEvent.ACTION_SCROLL:
1589 if (mNativeContentViewCore == 0) return false;
1591 nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
1592 event.getX(), event.getY(),
1593 event.getAxisValue(MotionEvent.AXIS_VSCROLL));
1595 mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1596 // Send a delayed onMouseMove event so that we end
1597 // up hovering over the right position after the scroll.
1598 final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event);
1599 mFakeMouseMoveRunnable = new Runnable() {
1602 onHoverEvent(eventFakeMouseMove);
1603 eventFakeMouseMove.recycle();
1606 mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1610 return mContainerViewInternals.super_onGenericMotionEvent(event);
1614 * Sets the current amount to offset incoming touch events by. This is used to handle content
1615 * moving and not lining up properly with the android input system.
1616 * @param dx The X offset in pixels to shift touch events.
1617 * @param dy The Y offset in pixels to shift touch events.
1619 public void setCurrentMotionEventOffsets(float dx, float dy) {
1620 mCurrentTouchOffsetX = dx;
1621 mCurrentTouchOffsetY = dy;
1624 private MotionEvent createOffsetMotionEvent(MotionEvent src) {
1625 MotionEvent dst = MotionEvent.obtain(src);
1626 dst.offsetLocation(mCurrentTouchOffsetX, mCurrentTouchOffsetY);
1631 * @see View#scrollBy(int, int)
1632 * Currently the ContentView scrolling happens in the native side. In
1633 * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
1634 * are overridden, so that View's mScrollX and mScrollY will be unchanged at
1635 * (0, 0). This is critical for drawing ContentView correctly.
1637 public void scrollBy(int xPix, int yPix) {
1638 if (mNativeContentViewCore != 0) {
1639 nativeScrollBy(mNativeContentViewCore,
1640 SystemClock.uptimeMillis(), 0, 0, xPix, yPix);
1645 * @see View#scrollTo(int, int)
1647 public void scrollTo(int xPix, int yPix) {
1648 if (mNativeContentViewCore == 0) return;
1649 final float xCurrentPix = mRenderCoordinates.getScrollXPix();
1650 final float yCurrentPix = mRenderCoordinates.getScrollYPix();
1651 final float dxPix = xPix - xCurrentPix;
1652 final float dyPix = yPix - yCurrentPix;
1653 if (dxPix != 0 || dyPix != 0) {
1654 long time = SystemClock.uptimeMillis();
1655 nativeScrollBegin(mNativeContentViewCore, time,
1656 xCurrentPix, yCurrentPix, -dxPix, -dyPix);
1657 nativeScrollBy(mNativeContentViewCore,
1658 time, xCurrentPix, yCurrentPix, dxPix, dyPix);
1659 nativeScrollEnd(mNativeContentViewCore, time);
1663 // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1665 public int getNativeScrollXForTest() {
1666 return mRenderCoordinates.getScrollXPixInt();
1669 // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1671 public int getNativeScrollYForTest() {
1672 return mRenderCoordinates.getScrollYPixInt();
1676 * @see View#computeHorizontalScrollExtent()
1678 @SuppressWarnings("javadoc")
1679 public int computeHorizontalScrollExtent() {
1680 return mRenderCoordinates.getLastFrameViewportWidthPixInt();
1684 * @see View#computeHorizontalScrollOffset()
1686 @SuppressWarnings("javadoc")
1687 public int computeHorizontalScrollOffset() {
1688 return mRenderCoordinates.getScrollXPixInt();
1692 * @see View#computeHorizontalScrollRange()
1694 @SuppressWarnings("javadoc")
1695 public int computeHorizontalScrollRange() {
1696 return mRenderCoordinates.getContentWidthPixInt();
1700 * @see View#computeVerticalScrollExtent()
1702 @SuppressWarnings("javadoc")
1703 public int computeVerticalScrollExtent() {
1704 return mRenderCoordinates.getLastFrameViewportHeightPixInt();
1708 * @see View#computeVerticalScrollOffset()
1710 @SuppressWarnings("javadoc")
1711 public int computeVerticalScrollOffset() {
1712 return mRenderCoordinates.getScrollYPixInt();
1716 * @see View#computeVerticalScrollRange()
1718 @SuppressWarnings("javadoc")
1719 public int computeVerticalScrollRange() {
1720 return mRenderCoordinates.getContentHeightPixInt();
1723 // End FrameLayout overrides.
1726 * @see View#awakenScrollBars(int, boolean)
1728 @SuppressWarnings("javadoc")
1729 public boolean awakenScrollBars(int startDelay, boolean invalidate) {
1730 // For the default implementation of ContentView which draws the scrollBars on the native
1731 // side, calling this function may get us into a bad state where we keep drawing the
1732 // scrollBars, so disable it by always returning false.
1733 if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
1736 return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
1740 private void updateForTapOrPress(int type, float xPix, float yPix) {
1741 if (type != GestureEventType.SINGLE_TAP_CONFIRMED
1742 && type != GestureEventType.SINGLE_TAP_UP
1743 && type != GestureEventType.LONG_PRESS
1744 && type != GestureEventType.LONG_TAP) {
1748 if (mContainerView.isFocusable() && mContainerView.isFocusableInTouchMode()
1749 && !mContainerView.isFocused()) {
1750 mContainerView.requestFocus();
1753 if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
1755 mLastTapX = (int) xPix;
1756 mLastTapY = (int) yPix;
1760 * @return The x coordinate for the last point that a tap or press gesture was initiated from.
1762 public int getLastTapX() {
1767 * @return The y coordinate for the last point that a tap or press gesture was initiated from.
1769 public int getLastTapY() {
1773 public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
1774 if (zoomControlsDelegate == null) {
1775 mZoomControlsDelegate = NO_OP_ZOOM_CONTROLS_DELEGATE;
1778 mZoomControlsDelegate = zoomControlsDelegate;
1781 public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
1782 if (mNativeContentViewCore == 0) return;
1783 nativeSetMultiTouchZoomSupportEnabled(mNativeContentViewCore, supportsMultiTouchZoom);
1786 public void updateDoubleTapSupport(boolean supportsDoubleTap) {
1787 if (mNativeContentViewCore == 0) return;
1788 nativeSetDoubleTapSupportEnabled(mNativeContentViewCore, supportsDoubleTap);
1791 public void selectPopupMenuItems(int[] indices) {
1792 if (mNativeContentViewCore != 0) {
1793 nativeSelectPopupMenuItems(mNativeContentViewCore, mNativeSelectPopupSourceFrame,
1796 mNativeSelectPopupSourceFrame = 0;
1797 mSelectPopup = null;
1801 * Send the screen orientation value to the renderer.
1804 void sendOrientationChangeEvent(int orientation) {
1805 if (mNativeContentViewCore == 0) return;
1807 nativeSendOrientationChangeEvent(mNativeContentViewCore, orientation);
1811 * Register the delegate to be used when content can not be handled by
1812 * the rendering engine, and should be downloaded instead. This will replace
1813 * the current delegate, if any.
1814 * @param delegate An implementation of ContentViewDownloadDelegate.
1816 public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
1817 mDownloadDelegate = delegate;
1820 // Called by DownloadController.
1821 ContentViewDownloadDelegate getDownloadDelegate() {
1822 return mDownloadDelegate;
1825 private void showSelectActionBar() {
1826 if (mActionMode != null) {
1827 mActionMode.invalidate();
1831 // Start a new action mode with a SelectActionModeCallback.
1832 SelectActionModeCallback.ActionHandler actionHandler =
1833 new SelectActionModeCallback.ActionHandler() {
1835 public void selectAll() {
1836 mImeAdapter.selectAll();
1845 public void copy() {
1850 public void paste() {
1851 mImeAdapter.paste();
1855 public void share() {
1856 final String query = getSelectedText();
1857 if (TextUtils.isEmpty(query)) return;
1859 Intent send = new Intent(Intent.ACTION_SEND);
1860 send.setType("text/plain");
1861 send.putExtra(Intent.EXTRA_TEXT, query);
1863 Intent i = Intent.createChooser(send, getContext().getString(
1864 R.string.actionbar_share));
1865 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1866 getContext().startActivity(i);
1867 } catch (android.content.ActivityNotFoundException ex) {
1868 // If no app handles it, do nothing.
1873 public void search() {
1874 final String query = getSelectedText();
1875 if (TextUtils.isEmpty(query)) return;
1877 // See if ContentViewClient wants to override
1878 if (getContentViewClient().doesPerformWebSearch()) {
1879 getContentViewClient().performWebSearch(query);
1883 Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
1884 i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
1885 i.putExtra(SearchManager.QUERY, query);
1886 i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
1887 if (!(getContext() instanceof Activity)) {
1888 i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1891 getContext().startActivity(i);
1892 } catch (android.content.ActivityNotFoundException ex) {
1893 // If no app handles it, do nothing.
1898 public boolean isSelectionPassword() {
1899 return mImeAdapter.isSelectionPassword();
1903 public boolean isSelectionEditable() {
1904 return mFocusedNodeEditable;
1908 public void onDestroyActionMode() {
1910 if (mUnselectAllOnActionModeDismiss) {
1912 if (isSelectionEditable()) {
1913 int selectionEnd = Selection.getSelectionEnd(mEditable);
1914 mInputConnection.setSelection(selectionEnd, selectionEnd);
1916 mImeAdapter.unselect();
1919 getContentViewClient().onContextualActionBarHidden();
1923 public boolean isShareAvailable() {
1924 Intent intent = new Intent(Intent.ACTION_SEND);
1925 intent.setType("text/plain");
1926 return getContext().getPackageManager().queryIntentActivities(intent,
1927 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
1931 public boolean isWebSearchAvailable() {
1932 if (getContentViewClient().doesPerformWebSearch()) return true;
1933 Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
1934 intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
1935 return getContext().getPackageManager().queryIntentActivities(intent,
1936 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
1940 // On ICS, startActionMode throws an NPE when getParent() is null.
1941 if (mContainerView.getParent() != null) {
1942 assert mWebContents != null;
1943 mActionMode = mContainerView.startActionMode(
1944 getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler,
1945 mWebContents.isIncognito()));
1947 mUnselectAllOnActionModeDismiss = true;
1948 if (mActionMode == null) {
1949 // There is no ActionMode, so remove the selection.
1950 mImeAdapter.unselect();
1952 getContentViewClient().onContextualActionBarShown();
1957 * Clears the current text selection.
1959 public void clearSelection() {
1960 mImeAdapter.unselect();
1964 * Ensure the selection is preserved the next time the view loses focus.
1966 public void preserveSelectionOnNextLossOfFocus() {
1967 mPreserveSelectionOnNextLossOfFocus = true;
1971 * @return Whether the page has an active, touch-controlled selection region.
1974 public boolean hasSelection() {
1975 return mHasSelection;
1978 private void hidePastePopup() {
1979 if (mPastePopupMenu == null) return;
1980 mPastePopupMenu.hide();
1984 private void onSelectionEvent(int eventType, float posXDip, float posYDip) {
1985 switch (eventType) {
1986 case SelectionEventType.SELECTION_SHOWN:
1987 mHasSelection = true;
1988 mUnselectAllOnActionModeDismiss = true;
1989 // TODO(cjhopman): Remove this when there is a better signal that long press caused
1990 // a selection. See http://crbug.com/150151.
1991 mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
1992 showSelectActionBar();
1995 case SelectionEventType.SELECTION_CLEARED:
1996 mHasSelection = false;
1997 mUnselectAllOnActionModeDismiss = false;
1998 hideSelectActionBar();
2001 case SelectionEventType.SELECTION_DRAG_STARTED:
2004 case SelectionEventType.SELECTION_DRAG_STOPPED:
2007 case SelectionEventType.INSERTION_SHOWN:
2008 mHasInsertion = true;
2011 case SelectionEventType.INSERTION_MOVED:
2012 if (mPastePopupMenu == null) break;
2013 if (!isScrollInProgress() && mPastePopupMenu.isShowing()) {
2014 showPastePopup((int) posXDip, (int) posYDip);
2020 case SelectionEventType.INSERTION_TAPPED:
2021 if (mWasPastePopupShowingOnInsertionDragStart)
2024 showPastePopup((int) posXDip, (int) posYDip);
2027 case SelectionEventType.INSERTION_CLEARED:
2028 mHasInsertion = false;
2032 case SelectionEventType.INSERTION_DRAG_STARTED:
2033 mWasPastePopupShowingOnInsertionDragStart =
2034 mPastePopupMenu != null && mPastePopupMenu.isShowing();
2039 assert false : "Invalid selection event type.";
2042 final float scale = mRenderCoordinates.getDeviceScaleFactor();
2043 getContentViewClient().onSelectionEvent(eventType, posXDip * scale, posYDip * scale);
2046 private void hideTextHandles() {
2047 mHasSelection = false;
2048 mHasInsertion = false;
2049 if (mNativeContentViewCore != 0) nativeHideTextHandles(mNativeContentViewCore);
2053 * Hides the IME if the containerView is the active view for IME.
2055 public void hideImeIfNeeded() {
2056 // Hide input method window from the current view synchronously
2057 // because ImeAdapter does so asynchronouly with a delay, and
2058 // by the time when ImeAdapter dismisses the input, the
2059 // containerView may have lost focus.
2060 // We cannot trust ContentViewClient#onImeStateChangeRequested to
2061 // hide the input window because it has an empty default implementation.
2062 // So we need to explicitly hide the input method window here.
2063 if (mInputMethodManagerWrapper.isActive(mContainerView)) {
2064 mInputMethodManagerWrapper.hideSoftInputFromWindow(
2065 mContainerView.getWindowToken(), 0, null);
2067 getContentViewClient().onImeStateChangeRequested(false);
2070 @SuppressWarnings("unused")
2072 private void updateFrameInfo(
2073 float scrollOffsetX, float scrollOffsetY,
2074 float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
2075 float contentWidth, float contentHeight,
2076 float viewportWidth, float viewportHeight,
2077 float controlsOffsetYCss, float contentOffsetYCss) {
2078 TraceEvent.begin("ContentViewCore:updateFrameInfo");
2079 // Adjust contentWidth/Height to be always at least as big as
2080 // the actual viewport (as set by onSizeChanged).
2081 final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2082 contentWidth = Math.max(contentWidth,
2083 mViewportWidthPix / (deviceScale * pageScaleFactor));
2084 contentHeight = Math.max(contentHeight,
2085 mViewportHeightPix / (deviceScale * pageScaleFactor));
2086 final float contentOffsetYPix = mRenderCoordinates.fromDipToPix(contentOffsetYCss);
2088 final boolean contentSizeChanged =
2089 contentWidth != mRenderCoordinates.getContentWidthCss()
2090 || contentHeight != mRenderCoordinates.getContentHeightCss();
2091 final boolean scaleLimitsChanged =
2092 minPageScaleFactor != mRenderCoordinates.getMinPageScaleFactor()
2093 || maxPageScaleFactor != mRenderCoordinates.getMaxPageScaleFactor();
2094 final boolean pageScaleChanged =
2095 pageScaleFactor != mRenderCoordinates.getPageScaleFactor();
2096 final boolean scrollChanged =
2098 || scrollOffsetX != mRenderCoordinates.getScrollX()
2099 || scrollOffsetY != mRenderCoordinates.getScrollY();
2100 final boolean contentOffsetChanged =
2101 contentOffsetYPix != mRenderCoordinates.getContentOffsetYPix();
2103 final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
2104 final boolean needUpdateZoomControls = scaleLimitsChanged || scrollChanged;
2106 if (needHidePopupZoomer) mPopupZoomer.hide(true);
2108 if (scrollChanged) {
2109 mContainerViewInternals.onScrollChanged(
2110 (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetX),
2111 (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetY),
2112 (int) mRenderCoordinates.getScrollXPix(),
2113 (int) mRenderCoordinates.getScrollYPix());
2116 mRenderCoordinates.updateFrameInfo(
2117 scrollOffsetX, scrollOffsetY,
2118 contentWidth, contentHeight,
2119 viewportWidth, viewportHeight,
2120 pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
2123 if (scrollChanged || contentOffsetChanged) {
2124 for (mGestureStateListenersIterator.rewind();
2125 mGestureStateListenersIterator.hasNext();) {
2126 mGestureStateListenersIterator.next().onScrollOffsetOrExtentChanged(
2127 computeVerticalScrollOffset(),
2128 computeVerticalScrollExtent());
2132 if (needUpdateZoomControls) mZoomControlsDelegate.updateZoomControls();
2134 // Update offsets for fullscreen.
2135 final float controlsOffsetPix = controlsOffsetYCss * deviceScale;
2136 // TODO(aelias): Remove last argument after downstream removes it.
2137 getContentViewClient().onOffsetsForFullscreenChanged(
2138 controlsOffsetPix, contentOffsetYPix, 0);
2140 if (mBrowserAccessibilityManager != null) {
2141 mBrowserAccessibilityManager.notifyFrameInfoInitialized();
2143 TraceEvent.end("ContentViewCore:updateFrameInfo");
2147 private void updateImeAdapter(long nativeImeAdapterAndroid, int textInputType,
2148 int textInputFlags, String text, int selectionStart, int selectionEnd,
2149 int compositionStart, int compositionEnd, boolean showImeIfNeeded,
2150 boolean isNonImeChange) {
2152 mFocusedNodeEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
2153 if (!mFocusedNodeEditable) hidePastePopup();
2155 mImeAdapter.updateKeyboardVisibility(
2156 nativeImeAdapterAndroid, textInputType, textInputFlags, showImeIfNeeded);
2158 if (mInputConnection != null) {
2159 mInputConnection.updateState(text, selectionStart, selectionEnd, compositionStart,
2160 compositionEnd, isNonImeChange);
2163 if (mActionMode != null) mActionMode.invalidate();
2167 @SuppressWarnings("unused")
2169 private void setTitle(String title) {
2170 getContentViewClient().onUpdateTitle(title);
2174 * Called (from native) when the <select> popup needs to be shown.
2175 * @param nativeSelectPopupSourceFrame The native RenderFrameHost that owns the popup.
2176 * @param items Items to show.
2177 * @param enabled POPUP_ITEM_TYPEs for items.
2178 * @param multiple Whether the popup menu should support multi-select.
2179 * @param selectedIndices Indices of selected items.
2181 @SuppressWarnings("unused")
2183 private void showSelectPopup(long nativeSelectPopupSourceFrame, Rect bounds, String[] items,
2184 int[] enabled, boolean multiple, int[] selectedIndices) {
2185 if (mContainerView.getParent() == null || mContainerView.getVisibility() != View.VISIBLE) {
2186 mNativeSelectPopupSourceFrame = nativeSelectPopupSourceFrame;
2187 selectPopupMenuItems(null);
2191 hidePopupsAndClearSelection();
2192 assert mNativeSelectPopupSourceFrame == 0 : "Zombie popup did not clear the frame source";
2194 assert items.length == enabled.length;
2195 List<SelectPopupItem> popupItems = new ArrayList<SelectPopupItem>();
2196 for (int i = 0; i < items.length; i++) {
2197 popupItems.add(new SelectPopupItem(items[i], enabled[i]));
2199 if (DeviceFormFactor.isTablet(mContext) && !multiple) {
2200 mSelectPopup = new SelectPopupDropdown(this, popupItems, bounds, selectedIndices);
2202 mSelectPopup = new SelectPopupDialog(this, popupItems, multiple, selectedIndices);
2204 mNativeSelectPopupSourceFrame = nativeSelectPopupSourceFrame;
2205 mSelectPopup.show();
2209 * Called when the <select> popup needs to be hidden.
2212 private void hideSelectPopup() {
2213 if (mSelectPopup != null) mSelectPopup.hide();
2217 * @return The visible select popup being shown.
2219 public SelectPopup getSelectPopupForTest() {
2220 return mSelectPopup;
2223 @SuppressWarnings("unused")
2225 private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
2226 mPopupZoomer.setBitmap(zoomedBitmap);
2227 mPopupZoomer.show(targetRect);
2230 @SuppressWarnings("unused")
2232 private TouchEventSynthesizer createTouchEventSynthesizer() {
2233 return new TouchEventSynthesizer(this);
2236 @SuppressWarnings("unused")
2238 private PopupTouchHandleDrawable createPopupTouchHandleDrawable() {
2239 if (mTouchHandleDelegate == null) {
2240 mTouchHandleDelegate = new PopupTouchHandleDrawableDelegate() {
2242 public View getParent() {
2243 return getContainerView();
2247 public PositionObserver getParentPositionObserver() {
2248 return mPositionObserver;
2252 public boolean onTouchHandleEvent(MotionEvent event) {
2253 final boolean isTouchHandleEvent = true;
2254 return onTouchEventImpl(event, isTouchHandleEvent);
2258 public boolean isScrollInProgress() {
2259 return ContentViewCore.this.isScrollInProgress();
2263 return new PopupTouchHandleDrawable(mTouchHandleDelegate);
2266 @SuppressWarnings("unused")
2268 private void onSelectionChanged(String text) {
2269 mLastSelectedText = text;
2270 getContentViewClient().onSelectionChanged(text);
2273 @SuppressWarnings("unused")
2275 private void showPastePopupWithFeedback(int xDip, int yDip) {
2276 // TODO(jdduke): Remove this when there is a better signal that long press caused
2277 // showing of the paste popup. See http://crbug.com/150151.
2278 if (showPastePopup(xDip, yDip)) {
2279 mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2283 private boolean showPastePopup(int xDip, int yDip) {
2284 if (!mHasInsertion || !canPaste()) return false;
2285 final float contentOffsetYPix = mRenderCoordinates.getContentOffsetYPix();
2286 getPastePopup().showAt(
2287 (int) mRenderCoordinates.fromDipToPix(xDip),
2288 (int) (mRenderCoordinates.fromDipToPix(yDip) + contentOffsetYPix));
2292 private PastePopupMenu getPastePopup() {
2293 if (mPastePopupMenu == null) {
2294 mPastePopupMenu = new PastePopupMenu(getContainerView(),
2295 new PastePopupMenuDelegate() {
2297 public void paste() {
2298 mImeAdapter.paste();
2303 return mPastePopupMenu;
2307 public PastePopupMenu getPastePopupForTest() {
2308 return getPastePopup();
2311 private boolean canPaste() {
2312 if (!mFocusedNodeEditable) return false;
2313 return ((ClipboardManager) mContext.getSystemService(
2314 Context.CLIPBOARD_SERVICE)).hasPrimaryClip();
2317 @SuppressWarnings("unused")
2319 private void onRenderProcessChange() {
2324 * Attaches the native ImeAdapter object to the java ImeAdapter to allow communication via JNI.
2326 public void attachImeAdapter() {
2327 if (mImeAdapter != null && mNativeContentViewCore != 0) {
2328 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2333 * @see View#hasFocus()
2336 public boolean hasFocus() {
2337 return mContainerView.hasFocus();
2341 * Checks whether the ContentViewCore can be zoomed in.
2343 * @return True if the ContentViewCore can be zoomed in.
2345 // This method uses the term 'zoom' for legacy reasons, but relates
2346 // to what chrome calls the 'page scale factor'.
2347 public boolean canZoomIn() {
2348 final float zoomInExtent = mRenderCoordinates.getMaxPageScaleFactor()
2349 - mRenderCoordinates.getPageScaleFactor();
2350 return zoomInExtent > ZOOM_CONTROLS_EPSILON;
2354 * Checks whether the ContentViewCore can be zoomed out.
2356 * @return True if the ContentViewCore can be zoomed out.
2358 // This method uses the term 'zoom' for legacy reasons, but relates
2359 // to what chrome calls the 'page scale factor'.
2360 public boolean canZoomOut() {
2361 final float zoomOutExtent = mRenderCoordinates.getPageScaleFactor()
2362 - mRenderCoordinates.getMinPageScaleFactor();
2363 return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
2367 * Zooms in the ContentViewCore by 25% (or less if that would result in
2368 * zooming in more than possible).
2370 * @return True if there was a zoom change, false otherwise.
2372 // This method uses the term 'zoom' for legacy reasons, but relates
2373 // to what chrome calls the 'page scale factor'.
2374 public boolean zoomIn() {
2378 return pinchByDelta(1.25f);
2382 * Zooms out the ContentViewCore by 20% (or less if that would result in
2383 * zooming out more than possible).
2385 * @return True if there was a zoom change, false otherwise.
2387 // This method uses the term 'zoom' for legacy reasons, but relates
2388 // to what chrome calls the 'page scale factor'.
2389 public boolean zoomOut() {
2390 if (!canZoomOut()) {
2393 return pinchByDelta(0.8f);
2397 * Resets the zoom factor of the ContentViewCore.
2399 * @return True if there was a zoom change, false otherwise.
2401 // This method uses the term 'zoom' for legacy reasons, but relates
2402 // to what chrome calls the 'page scale factor'.
2403 public boolean zoomReset() {
2404 // The page scale factor is initialized to mNativeMinimumScale when
2405 // the page finishes loading. Thus sets it back to mNativeMinimumScale.
2406 if (!canZoomOut()) return false;
2407 return pinchByDelta(
2408 mRenderCoordinates.getMinPageScaleFactor()
2409 / mRenderCoordinates.getPageScaleFactor());
2413 * Simulate a pinch zoom gesture.
2415 * @param delta the factor by which the current page scale should be multiplied by.
2416 * @return whether the gesture was sent.
2418 public boolean pinchByDelta(float delta) {
2419 if (mNativeContentViewCore == 0) return false;
2421 long timeMs = SystemClock.uptimeMillis();
2422 int xPix = getViewportWidthPix() / 2;
2423 int yPix = getViewportHeightPix() / 2;
2425 nativePinchBegin(mNativeContentViewCore, timeMs, xPix, yPix);
2426 nativePinchBy(mNativeContentViewCore, timeMs, xPix, yPix, delta);
2427 nativePinchEnd(mNativeContentViewCore, timeMs);
2433 * Invokes the graphical zoom picker widget for this ContentView.
2435 public void invokeZoomPicker() {
2436 mZoomControlsDelegate.invokeZoomPicker();
2440 * Enables or disables inspection of JavaScript objects added via
2441 * {@link #addJavascriptInterface(Object, String)} by means of Object.keys() method and
2442 * "for .. in" loop. Being able to inspect JavaScript objects is useful
2443 * when debugging hybrid Android apps, but can't be enabled for legacy applications due
2444 * to compatibility risks.
2446 * @param allow Whether to allow JavaScript objects inspection.
2448 public void setAllowJavascriptInterfacesInspection(boolean allow) {
2449 nativeSetAllowJavascriptInterfacesInspection(mNativeContentViewCore, allow);
2453 * Returns JavaScript interface objects previously injected via
2454 * {@link #addJavascriptInterface(Object, String)}.
2456 * @return the mapping of names to interface objects and corresponding annotation classes
2458 public Map<String, Pair<Object, Class>> getJavascriptInterfaces() {
2459 return mJavaScriptInterfaces;
2463 * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
2464 * and automatically pass in {@link JavascriptInterface} as the required annotation.
2466 * @param object The Java object to inject into the ContentViewCore's JavaScript context. Null
2467 * values are ignored.
2468 * @param name The name used to expose the instance in JavaScript.
2470 public void addJavascriptInterface(Object object, String name) {
2471 addPossiblyUnsafeJavascriptInterface(object, name, JavascriptInterface.class);
2475 * This method injects the supplied Java object into the ContentViewCore.
2476 * The object is injected into the JavaScript context of the main frame,
2477 * using the supplied name. This allows the Java object to be accessed from
2478 * JavaScript. Note that that injected objects will not appear in
2479 * JavaScript until the page is next (re)loaded. For example:
2480 * <pre> view.addJavascriptInterface(new Object(), "injectedObject");
2481 * view.loadData("<!DOCTYPE html><title></title>", "text/html", null);
2482 * view.loadUrl("javascript:alert(injectedObject.toString())");</pre>
2483 * <p><strong>IMPORTANT:</strong>
2485 * <li> addJavascriptInterface() can be used to allow JavaScript to control
2486 * the host application. This is a powerful feature, but also presents a
2487 * security risk. Use of this method in a ContentViewCore containing
2488 * untrusted content could allow an attacker to manipulate the host
2489 * application in unintended ways, executing Java code with the permissions
2490 * of the host application. Use extreme care when using this method in a
2491 * ContentViewCore which could contain untrusted content. Particular care
2492 * should be taken to avoid unintentional access to inherited methods, such
2493 * as {@link Object#getClass()}. To prevent access to inherited methods,
2494 * pass an annotation for {@code requiredAnnotation}. This will ensure
2495 * that only methods with {@code requiredAnnotation} are exposed to the
2496 * Javascript layer. {@code requiredAnnotation} will be passed to all
2497 * subsequently injected Java objects if any methods return an object. This
2498 * means the same restrictions (or lack thereof) will apply. Alternatively,
2499 * {@link #addJavascriptInterface(Object, String)} can be called, which
2500 * automatically uses the {@link JavascriptInterface} annotation.
2501 * <li> JavaScript interacts with Java objects on a private, background
2502 * thread of the ContentViewCore. Care is therefore required to maintain
2503 * thread safety.</li>
2506 * @param object The Java object to inject into the
2507 * ContentViewCore's JavaScript context. Null
2508 * values are ignored.
2509 * @param name The name used to expose the instance in
2511 * @param requiredAnnotation Restrict exposed methods to ones with this
2512 * annotation. If {@code null} all methods are
2516 public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
2517 Class<? extends Annotation> requiredAnnotation) {
2518 if (mNativeContentViewCore != 0 && object != null) {
2519 mJavaScriptInterfaces.put(name, new Pair<Object, Class>(object, requiredAnnotation));
2520 nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation);
2525 * Removes a previously added JavaScript interface with the given name.
2527 * @param name The name of the interface to remove.
2529 public void removeJavascriptInterface(String name) {
2530 mJavaScriptInterfaces.remove(name);
2531 if (mNativeContentViewCore != 0) {
2532 nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2537 * Return the current scale of the ContentView.
2538 * @return The current page scale factor.
2541 public float getScale() {
2542 return mRenderCoordinates.getPageScaleFactor();
2546 * If the view is ready to draw contents to the screen. In hardware mode,
2547 * the initialization of the surface texture may not occur until after the
2548 * view has been added to the layout. This method will return {@code true}
2549 * once the texture is actually ready.
2551 public boolean isReady() {
2552 assert mWebContents != null;
2553 return mWebContents.isReady();
2557 private void startContentIntent(String contentUrl) {
2558 getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2562 public void onAccessibilityStateChanged(boolean enabled) {
2563 setAccessibilityState(enabled);
2567 * Determines whether or not this ContentViewCore can handle this accessibility action.
2568 * @param action The action to perform.
2569 * @return Whether or not this action is supported.
2571 public boolean supportsAccessibilityAction(int action) {
2572 return mAccessibilityInjector.supportsAccessibilityAction(action);
2576 * Attempts to perform an accessibility action on the web content. If the accessibility action
2577 * cannot be processed, it returns {@code null}, allowing the caller to know to call the
2578 * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value.
2579 * Otherwise the return value from this method should be used.
2580 * @param action The action to perform.
2581 * @param arguments Optional action arguments.
2582 * @return Whether the action was performed or {@code null} if the call should be delegated to
2583 * the super {@link View} class.
2585 public boolean performAccessibilityAction(int action, Bundle arguments) {
2586 if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2587 return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2594 * Set the BrowserAccessibilityManager, used for native accessibility
2595 * (not script injection). This is only set when system accessibility
2597 * @param manager The new BrowserAccessibilityManager.
2599 public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
2600 mBrowserAccessibilityManager = manager;
2604 * Get the BrowserAccessibilityManager, used for native accessibility
2605 * (not script injection). This will return null when system accessibility
2607 * @return This view's BrowserAccessibilityManager.
2609 public BrowserAccessibilityManager getBrowserAccessibilityManager() {
2610 return mBrowserAccessibilityManager;
2614 * If native accessibility (not script injection) is enabled, and if this is
2615 * running on JellyBean or later, returns an AccessibilityNodeProvider that
2616 * implements native accessibility for this view. Returns null otherwise.
2617 * Lazily initializes native accessibility here if it's allowed.
2618 * @return The AccessibilityNodeProvider, if available, or null otherwise.
2620 public AccessibilityNodeProvider getAccessibilityNodeProvider() {
2621 if (mBrowserAccessibilityManager != null) {
2622 return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
2625 if (mNativeAccessibilityAllowed &&
2626 !mNativeAccessibilityEnabled &&
2627 mNativeContentViewCore != 0 &&
2628 Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
2629 mNativeAccessibilityEnabled = true;
2630 nativeSetAccessibilityEnabled(mNativeContentViewCore, true);
2637 * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
2639 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2640 // Note: this is only used by the script-injecting accessibility code.
2641 mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
2645 * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
2647 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
2648 // Note: this is only used by the script-injecting accessibility code.
2649 event.setClassName(this.getClass().getName());
2651 // Identify where the top-left of the screen currently points to.
2652 event.setScrollX(mRenderCoordinates.getScrollXPixInt());
2653 event.setScrollY(mRenderCoordinates.getScrollYPixInt());
2655 // The maximum scroll values are determined by taking the content dimensions and
2656 // subtracting off the actual dimensions of the ChromeView.
2657 int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt());
2658 int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt());
2659 event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0);
2661 // Setting the maximum scroll values requires API level 15 or higher.
2662 final int sdkVersionRequiredToSetScroll = 15;
2663 if (Build.VERSION.SDK_INT >= sdkVersionRequiredToSetScroll) {
2664 event.setMaxScrollX(maxScrollXPix);
2665 event.setMaxScrollY(maxScrollYPix);
2670 * Returns whether accessibility script injection is enabled on the device
2672 public boolean isDeviceAccessibilityScriptInjectionEnabled() {
2674 // On JellyBean and higher, native accessibility is the default so script
2675 // injection is only allowed if enabled via a flag.
2676 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&
2677 !CommandLine.getInstance().hasSwitch(
2678 ContentSwitches.ENABLE_ACCESSIBILITY_SCRIPT_INJECTION)) {
2682 if (!mContentSettings.getJavaScriptEnabled()) {
2686 int result = getContext().checkCallingOrSelfPermission(
2687 android.Manifest.permission.INTERNET);
2688 if (result != PackageManager.PERMISSION_GRANTED) {
2692 Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
2693 field.setAccessible(true);
2694 String accessibilityScriptInjection = (String) field.get(null);
2695 ContentResolver contentResolver = getContext().getContentResolver();
2697 if (mAccessibilityScriptInjectionObserver == null) {
2698 ContentObserver contentObserver = new ContentObserver(new Handler()) {
2700 public void onChange(boolean selfChange, Uri uri) {
2701 setAccessibilityState(mAccessibilityManager.isEnabled());
2704 contentResolver.registerContentObserver(
2705 Settings.Secure.getUriFor(accessibilityScriptInjection),
2708 mAccessibilityScriptInjectionObserver = contentObserver;
2711 return Settings.Secure.getInt(contentResolver, accessibilityScriptInjection, 0) == 1;
2712 } catch (NoSuchFieldException e) {
2713 // Do nothing, default to false.
2714 } catch (IllegalAccessException e) {
2715 // Do nothing, default to false.
2721 * Returns whether or not accessibility injection is being used.
2723 public boolean isInjectingAccessibilityScript() {
2724 return mAccessibilityInjector.accessibilityIsAvailable();
2728 * Returns true if accessibility is on and touch exploration is enabled.
2730 public boolean isTouchExplorationEnabled() {
2731 return mTouchExplorationEnabled;
2735 * Turns browser accessibility on or off.
2736 * If |state| is |false|, this turns off both native and injected accessibility.
2737 * Otherwise, if accessibility script injection is enabled, this will enable the injected
2738 * accessibility scripts. Native accessibility is enabled on demand.
2740 public void setAccessibilityState(boolean state) {
2742 setInjectedAccessibility(false);
2743 mNativeAccessibilityAllowed = false;
2744 mTouchExplorationEnabled = false;
2746 boolean useScriptInjection = isDeviceAccessibilityScriptInjectionEnabled();
2747 setInjectedAccessibility(useScriptInjection);
2748 mNativeAccessibilityAllowed = !useScriptInjection;
2749 mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
2754 * Enable or disable injected accessibility features
2756 public void setInjectedAccessibility(boolean enabled) {
2757 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
2758 mAccessibilityInjector.setScriptEnabled(enabled);
2762 * Stop any TTS notifications that are currently going on.
2764 public void stopCurrentAccessibilityNotifications() {
2765 mAccessibilityInjector.onPageLostFocus();
2769 * Return whether or not we should set accessibility focus on page load.
2771 public boolean shouldSetAccessibilityFocusOnPageLoad() {
2772 return mShouldSetAccessibilityFocusOnPageLoad;
2776 * Sets whether or not we should set accessibility focus on page load.
2777 * This only applies if an accessibility service like TalkBack is running.
2778 * This is desirable behavior for a browser window, but not for an embedded
2781 public void setShouldSetAccessibilityFocusOnPageLoad(boolean on) {
2782 mShouldSetAccessibilityFocusOnPageLoad = on;
2786 * Inform WebKit that Fullscreen mode has been exited by the user.
2788 public void exitFullscreen() {
2789 assert mWebContents != null;
2790 mWebContents.exitFullscreen();
2794 * Changes whether hiding the top controls is enabled.
2796 * @param enableHiding Whether hiding the top controls should be enabled or not.
2797 * @param enableShowing Whether showing the top controls should be enabled or not.
2798 * @param animate Whether the transition should be animated or not.
2800 public void updateTopControlsState(boolean enableHiding, boolean enableShowing,
2802 assert mWebContents != null;
2803 mWebContents.updateTopControlsState(
2804 enableHiding, enableShowing, animate);
2808 * @return The cached copy of render positions and scales.
2810 public RenderCoordinates getRenderCoordinates() {
2811 return mRenderCoordinates;
2815 private static Rect createRect(int x, int y, int right, int bottom) {
2816 return new Rect(x, y, right, bottom);
2819 public void extractSmartClipData(int x, int y, int width, int height) {
2820 if (mNativeContentViewCore != 0) {
2821 x += mSmartClipOffsetX;
2822 y += mSmartClipOffsetY;
2823 nativeExtractSmartClipData(mNativeContentViewCore, x, y, width, height);
2828 * Set offsets for smart clip.
2830 * <p>This should be called if there is a viewport change introduced by,
2831 * e.g., show and hide of a location bar.
2833 * @param offsetX Offset for X position.
2834 * @param offsetY Offset for Y position.
2836 public void setSmartClipOffsets(int offsetX, int offsetY) {
2837 mSmartClipOffsetX = offsetX;
2838 mSmartClipOffsetY = offsetY;
2842 private void onSmartClipDataExtracted(String text, String html, Rect clipRect) {
2843 // Translate the positions by the offsets introduced by location bar. Note that the
2844 // coordinates are in dp scale, and that this definitely has the potential to be
2845 // different from the offsets when extractSmartClipData() was called. However,
2846 // as long as OEM has a UI that consumes all the inputs and waits until the
2847 // callback is called, then there shouldn't be any difference.
2848 // TODO(changwan): once crbug.com/416432 is resolved, try to pass offsets as
2849 // separate params for extractSmartClipData(), and apply them not the new offset
2850 // values in the callback.
2851 final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2852 final int offsetXInDp = (int) (mSmartClipOffsetX / deviceScale);
2853 final int offsetYInDp = (int) (mSmartClipOffsetY / deviceScale);
2854 clipRect.offset(-offsetXInDp, -offsetYInDp);
2856 if (mSmartClipDataListener != null) {
2857 mSmartClipDataListener.onSmartClipDataExtracted(text, html, clipRect);
2861 public void setSmartClipDataListener(SmartClipDataListener listener) {
2862 mSmartClipDataListener = listener;
2865 public void setBackgroundOpaque(boolean opaque) {
2866 if (mNativeContentViewCore != 0) {
2867 nativeSetBackgroundOpaque(mNativeContentViewCore, opaque);
2872 * Offer a long press gesture to the embedding View, primarily for WebView compatibility.
2874 * @return true if the embedder handled the event.
2876 private boolean offerLongPressToEmbedder() {
2877 return mContainerView.performLongClick();
2881 * Reset scroll and fling accounting, notifying listeners as appropriate.
2882 * This is useful as a failsafe when the input stream may have been interruped.
2884 private void resetScrollInProgress() {
2885 if (!isScrollInProgress()) return;
2887 final boolean touchScrollInProgress = mTouchScrollInProgress;
2888 final int potentiallyActiveFlingCount = mPotentiallyActiveFlingCount;
2890 mTouchScrollInProgress = false;
2891 mPotentiallyActiveFlingCount = 0;
2893 if (touchScrollInProgress) updateGestureStateListener(GestureEventType.SCROLL_END);
2894 if (potentiallyActiveFlingCount > 0) updateGestureStateListener(GestureEventType.FLING_END);
2897 private native long nativeInit(long webContentsPtr,
2898 long viewAndroidPtr, long windowAndroidPtr, HashSet<Object> retainedObjectSet);
2901 private ContentVideoViewClient getContentVideoViewClient() {
2902 return getContentViewClient().getContentVideoViewClient();
2906 private boolean shouldBlockMediaRequest(String url) {
2907 return getContentViewClient().shouldBlockMediaRequest(url);
2911 private void onNativeFlingStopped() {
2912 // Note that mTouchScrollInProgress should normally be false at this
2913 // point, but we reset it anyway as another failsafe.
2914 mTouchScrollInProgress = false;
2915 if (mPotentiallyActiveFlingCount <= 0) return;
2916 mPotentiallyActiveFlingCount--;
2917 updateGestureStateListener(GestureEventType.FLING_END);
2921 public void onScreenOrientationChanged(int orientation) {
2922 sendOrientationChangeEvent(orientation);
2925 public void resumeResponseDeferredAtStart() {
2926 assert mWebContents != null;
2927 mWebContents.resumeResponseDeferredAtStart();
2931 * Set whether the ContentViewCore requires the WebContents to be fullscreen in order to lock
2932 * the screen orientation.
2934 public void setFullscreenRequiredForOrientationLock(boolean value) {
2935 mFullscreenRequiredForOrientationLock = value;
2939 private boolean isFullscreenRequiredForOrientationLock() {
2940 return mFullscreenRequiredForOrientationLock;
2943 private native WebContents nativeGetWebContentsAndroid(long nativeContentViewCoreImpl);
2945 private native void nativeOnJavaContentViewCoreDestroyed(long nativeContentViewCoreImpl);
2947 private native void nativeSetFocus(long nativeContentViewCoreImpl, boolean focused);
2949 private native void nativeSendOrientationChangeEvent(
2950 long nativeContentViewCoreImpl, int orientation);
2952 // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
2953 private native boolean nativeOnTouchEvent(
2954 long nativeContentViewCoreImpl, MotionEvent event,
2955 long timeMs, int action, int pointerCount, int historySize, int actionIndex,
2956 float x0, float y0, float x1, float y1,
2957 int pointerId0, int pointerId1,
2958 float touchMajor0, float touchMajor1,
2959 float touchMinor0, float touchMinor1,
2960 float orientation0, float orientation1,
2961 float rawX, float rawY,
2962 int androidToolType0, int androidToolType1,
2963 int androidButtonState, int androidMetaState,
2964 boolean isTouchHandleEvent);
2966 private native int nativeSendMouseMoveEvent(
2967 long nativeContentViewCoreImpl, long timeMs, float x, float y);
2969 private native int nativeSendMouseWheelEvent(
2970 long nativeContentViewCoreImpl, long timeMs, float x, float y, float verticalAxis);
2972 private native void nativeScrollBegin(
2973 long nativeContentViewCoreImpl, long timeMs, float x, float y, float hintX,
2976 private native void nativeScrollEnd(long nativeContentViewCoreImpl, long timeMs);
2978 private native void nativeScrollBy(
2979 long nativeContentViewCoreImpl, long timeMs, float x, float y,
2980 float deltaX, float deltaY);
2982 private native void nativeFlingStart(
2983 long nativeContentViewCoreImpl, long timeMs, float x, float y, float vx, float vy);
2985 private native void nativeFlingCancel(long nativeContentViewCoreImpl, long timeMs);
2987 private native void nativeSingleTap(
2988 long nativeContentViewCoreImpl, long timeMs, float x, float y);
2990 private native void nativeDoubleTap(
2991 long nativeContentViewCoreImpl, long timeMs, float x, float y);
2993 private native void nativeLongPress(
2994 long nativeContentViewCoreImpl, long timeMs, float x, float y);
2996 private native void nativePinchBegin(
2997 long nativeContentViewCoreImpl, long timeMs, float x, float y);
2999 private native void nativePinchEnd(long nativeContentViewCoreImpl, long timeMs);
3001 private native void nativePinchBy(long nativeContentViewCoreImpl, long timeMs,
3002 float anchorX, float anchorY, float deltaScale);
3004 private native void nativeSelectBetweenCoordinates(
3005 long nativeContentViewCoreImpl, float x1, float y1, float x2, float y2);
3007 private native void nativeMoveCaret(long nativeContentViewCoreImpl, float x, float y);
3009 private native void nativeHideTextHandles(long nativeContentViewCoreImpl);
3011 private native void nativeResetGestureDetection(long nativeContentViewCoreImpl);
3013 private native void nativeSetDoubleTapSupportEnabled(
3014 long nativeContentViewCoreImpl, boolean enabled);
3016 private native void nativeSetMultiTouchZoomSupportEnabled(
3017 long nativeContentViewCoreImpl, boolean enabled);
3019 private native void nativeSelectPopupMenuItems(long nativeContentViewCoreImpl,
3020 long nativeSelectPopupSourceFrame, int[] indices);
3023 private native void nativePostMessageToFrame(long nativeContentViewCoreImpl, String frameId,
3024 String message, String sourceOrigin, String targetOrigin);
3026 private native long nativeGetNativeImeAdapter(long nativeContentViewCoreImpl);
3028 private native int nativeGetCurrentRenderProcessId(long nativeContentViewCoreImpl);
3030 private native void nativeSetAllowJavascriptInterfacesInspection(
3031 long nativeContentViewCoreImpl, boolean allow);
3033 private native void nativeAddJavascriptInterface(long nativeContentViewCoreImpl, Object object,
3034 String name, Class requiredAnnotation);
3036 private native void nativeRemoveJavascriptInterface(long nativeContentViewCoreImpl,
3039 private native void nativeWasResized(long nativeContentViewCoreImpl);
3041 private native void nativeSetAccessibilityEnabled(
3042 long nativeContentViewCoreImpl, boolean enabled);
3044 private native void nativeExtractSmartClipData(long nativeContentViewCoreImpl,
3045 int x, int y, int w, int h);
3047 private native void nativeSetBackgroundOpaque(long nativeContentViewCoreImpl, boolean opaque);