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.WebContents;
79 import org.chromium.ui.base.DeviceFormFactor;
80 import org.chromium.ui.base.ViewAndroid;
81 import org.chromium.ui.base.ViewAndroidDelegate;
82 import org.chromium.ui.base.WindowAndroid;
83 import org.chromium.ui.gfx.DeviceDisplayInfo;
85 import java.lang.annotation.Annotation;
86 import java.lang.reflect.Field;
87 import java.util.ArrayList;
88 import java.util.HashMap;
89 import java.util.HashSet;
90 import java.util.LinkedHashMap;
91 import java.util.List;
93 import java.util.Map.Entry;
96 * Provides a Java-side 'wrapper' around a WebContent (native) instance.
97 * Contains all the major functionality necessary to manage the lifecycle of a ContentView without
98 * being tied to the view system.
100 @JNINamespace("content")
101 public class ContentViewCore
102 implements AccessibilityStateChangeListener, ScreenOrientationObserver {
104 private static final String TAG = "ContentViewCore";
106 // Used to avoid enabling zooming in / out if resulting zooming will
107 // produce little visible difference.
108 private static final float ZOOM_CONTROLS_EPSILON = 0.007f;
110 // Used to represent gestures for long press and long tap.
111 private static final int IS_LONG_PRESS = 1;
112 private static final int IS_LONG_TAP = 2;
114 private static final ZoomControlsDelegate NO_OP_ZOOM_CONTROLS_DELEGATE =
115 new ZoomControlsDelegate() {
117 public void invokeZoomPicker() {}
119 public void dismissZoomPicker() {}
121 public void updateZoomControls() {}
124 // If the embedder adds a JavaScript interface object that contains an indirect reference to
125 // the ContentViewCore, then storing a strong ref to the interface object on the native
126 // side would prevent garbage collection of the ContentViewCore (as that strong ref would
127 // create a new GC root).
128 // For that reason, we store only a weak reference to the interface object on the
129 // native side. However we still need a strong reference on the Java side to
130 // prevent garbage collection if the embedder doesn't maintain their own ref to the
131 // interface object - the Java side ref won't create a new GC root.
132 // This map stores those references. We put into the map on addJavaScriptInterface()
133 // and remove from it in removeJavaScriptInterface(). The annotation class is stored for
134 // the purpose of migrating injected objects from one instance of CVC to another, which
135 // is used by Android WebView to support WebChromeClient.onCreateWindow scenario.
136 private final Map<String, Pair<Object, Class>> mJavaScriptInterfaces =
137 new HashMap<String, Pair<Object, Class>>();
139 // Additionally, we keep track of all Java bound JS objects that are in use on the
140 // current page to ensure that they are not garbage collected until the page is
141 // navigated. This includes interface objects that have been removed
142 // via the removeJavaScriptInterface API and transient objects returned from methods
143 // on the interface object. Note we use HashSet rather than Set as the native side
144 // expects HashSet (no bindings for interfaces).
145 private final HashSet<Object> mRetainedJavaScriptObjects = new HashSet<Object>();
148 * A {@link ViewAndroidDelegate} that delegates to the current container view.
150 * <p>This delegate handles the replacement of container views transparently so
151 * that clients can safely hold to instances of this class.
153 private class ContentViewAndroidDelegate implements ViewAndroidDelegate {
155 * Represents the position of an anchor view.
158 private class Position {
159 private final float mX;
160 private final float mY;
161 private final float mWidth;
162 private final float mHeight;
164 public Position(float x, float y, float width, float height) {
173 * The current container view. This view can be updated with
174 * {@link #updateCurrentContainerView()}.
176 private ViewGroup mCurrentContainerView;
179 * List of anchor views stored in the order in which they were acquired mapped
182 private Map<View, Position> mAnchorViews = new LinkedHashMap<View, Position>();
185 public View acquireAnchorView() {
186 View anchorView = new View(mContext);
187 mAnchorViews.put(anchorView, null);
188 mCurrentContainerView.addView(anchorView);
193 public void setAnchorViewPosition(
194 View view, float x, float y, float width, float height) {
195 mAnchorViews.put(view, new Position(x, y, width, height));
196 doSetAnchorViewPosition(view, x, y, width, height);
199 @SuppressWarnings("deprecation") // AbsoluteLayout
200 private void doSetAnchorViewPosition(
201 View view, float x, float y, float width, float height) {
202 if (view.getParent() == null) {
203 // Ignore. setAnchorViewPosition has been called after the anchor view has
204 // already been released.
207 assert view.getParent() == mCurrentContainerView;
209 float scale = (float) DeviceDisplayInfo.create(mContext).getDIPScale();
211 // The anchor view should not go outside the bounds of the ContainerView.
212 int leftMargin = Math.round(x * scale);
213 int topMargin = Math.round(mRenderCoordinates.getContentOffsetYPix() + y * scale);
214 int scaledWidth = Math.round(width * scale);
215 // ContentViewCore currently only supports these two container view types.
216 if (mCurrentContainerView instanceof FrameLayout) {
218 if (ApiCompatibilityUtils.isLayoutRtl(mCurrentContainerView)) {
219 startMargin = mCurrentContainerView.getMeasuredWidth()
220 - Math.round((width + x) * scale);
222 startMargin = leftMargin;
224 if (scaledWidth + startMargin > mCurrentContainerView.getWidth()) {
225 scaledWidth = mCurrentContainerView.getWidth() - startMargin;
227 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
228 scaledWidth, Math.round(height * scale));
229 ApiCompatibilityUtils.setMarginStart(lp, startMargin);
230 lp.topMargin = topMargin;
231 view.setLayoutParams(lp);
232 } else if (mCurrentContainerView instanceof android.widget.AbsoluteLayout) {
233 // This fixes the offset due to a difference in
234 // scrolling model of WebView vs. Chrome.
235 // TODO(sgurun) fix this to use mContainerViewAtCreation.getScroll[X/Y]()
236 // as it naturally accounts for scroll differences between
238 leftMargin += mRenderCoordinates.getScrollXPixInt();
239 topMargin += mRenderCoordinates.getScrollYPixInt();
241 android.widget.AbsoluteLayout.LayoutParams lp =
242 new android.widget.AbsoluteLayout.LayoutParams(
243 scaledWidth, (int) (height * scale), leftMargin, topMargin);
244 view.setLayoutParams(lp);
246 Log.e(TAG, "Unknown layout " + mCurrentContainerView.getClass().getName());
251 public void releaseAnchorView(View anchorView) {
252 mAnchorViews.remove(anchorView);
253 mCurrentContainerView.removeView(anchorView);
257 * Updates (or sets for the first time) the current container view to which
258 * this class delegates. Existing anchor views are transferred from the old to
259 * the new container view.
261 void updateCurrentContainerView() {
262 ViewGroup oldContainerView = mCurrentContainerView;
263 mCurrentContainerView = mContainerView;
264 for (Entry<View, Position> entry : mAnchorViews.entrySet()) {
265 View anchorView = entry.getKey();
266 Position position = entry.getValue();
267 oldContainerView.removeView(anchorView);
268 mCurrentContainerView.addView(anchorView);
269 if (position != null) {
270 doSetAnchorViewPosition(anchorView,
271 position.mX, position.mY, position.mWidth, position.mHeight);
278 * Interface that consumers of {@link ContentViewCore} must implement to allow the proper
279 * dispatching of view methods through the containing view.
282 * All methods with the "super_" prefix should be routed to the parent of the
283 * implementing container view.
285 @SuppressWarnings("javadoc")
286 public interface InternalAccessDelegate {
288 * @see View#drawChild(Canvas, View, long)
290 boolean drawChild(Canvas canvas, View child, long drawingTime);
293 * @see View#onKeyUp(keyCode, KeyEvent)
295 boolean super_onKeyUp(int keyCode, KeyEvent event);
298 * @see View#dispatchKeyEventPreIme(KeyEvent)
300 boolean super_dispatchKeyEventPreIme(KeyEvent event);
303 * @see View#dispatchKeyEvent(KeyEvent)
305 boolean super_dispatchKeyEvent(KeyEvent event);
308 * @see View#onGenericMotionEvent(MotionEvent)
310 boolean super_onGenericMotionEvent(MotionEvent event);
313 * @see View#onConfigurationChanged(Configuration)
315 void super_onConfigurationChanged(Configuration newConfig);
318 * @see View#onScrollChanged(int, int, int, int)
320 void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix);
323 * @see View#awakenScrollBars()
325 boolean awakenScrollBars();
328 * @see View#awakenScrollBars(int, boolean)
330 boolean super_awakenScrollBars(int startDelay, boolean invalidate);
334 * An interface for controlling visibility and state of embedder-provided zoom controls.
336 public interface ZoomControlsDelegate {
338 * Called when it's reasonable to show zoom controls.
340 void invokeZoomPicker();
343 * Called when zoom controls need to be hidden (e.g. when the view hides).
345 void dismissZoomPicker();
348 * Called when page scale has been changed, so the controls can update their state.
350 void updateZoomControls();
354 * An interface that allows the embedder to be notified when the results of
355 * extractSmartClipData are available.
357 public interface SmartClipDataListener {
358 public void onSmartClipDataExtracted(String text, String html, Rect clipRect);
361 private final Context mContext;
362 private ViewGroup mContainerView;
363 private InternalAccessDelegate mContainerViewInternals;
364 private WebContents mWebContents;
365 private WebContentsObserver mWebContentsObserver;
367 private ContentViewClient mContentViewClient;
369 private ContentSettings mContentSettings;
371 // Native pointer to C++ ContentViewCoreImpl object which will be set by nativeInit().
372 private long mNativeContentViewCore = 0;
374 private final ObserverList<GestureStateListener> mGestureStateListeners;
375 private final RewindableIterator<GestureStateListener> mGestureStateListenersIterator;
376 private ZoomControlsDelegate mZoomControlsDelegate;
378 private PopupZoomer mPopupZoomer;
379 private SelectPopup mSelectPopup;
380 private long mNativeSelectPopupSourceFrame = 0;
382 private Runnable mFakeMouseMoveRunnable = null;
384 // Only valid when focused on a text / password field.
385 private ImeAdapter mImeAdapter;
386 private ImeAdapter.AdapterInputConnectionFactory mAdapterInputConnectionFactory;
387 private AdapterInputConnection mInputConnection;
388 private InputMethodManagerWrapper mInputMethodManagerWrapper;
390 // Lazily created paste popup menu, triggered either via long press in an
391 // editable region or from tapping the insertion handle.
392 private PastePopupMenu mPastePopupMenu;
393 private boolean mWasPastePopupShowingOnInsertionDragStart;
395 private PopupTouchHandleDrawableDelegate mTouchHandleDelegate;
397 private PositionObserver mPositionObserver;
399 // Size of the viewport in physical pixels as set from onSizeChanged.
400 private int mViewportWidthPix;
401 private int mViewportHeightPix;
402 private int mPhysicalBackingWidthPix;
403 private int mPhysicalBackingHeightPix;
404 private int mTopControlsLayoutHeightPix;
406 // Cached copy of all positions and scales as reported by the renderer.
407 private final RenderCoordinates mRenderCoordinates;
409 // Tracks whether a selection is currently active. When applied to selected text, indicates
410 // whether the last selected text is still highlighted.
411 private boolean mHasSelection;
412 private boolean mHasInsertion;
413 private String mLastSelectedText;
414 private boolean mFocusedNodeEditable;
415 private ActionMode mActionMode;
416 private boolean mUnselectAllOnActionModeDismiss;
417 private boolean mPreserveSelectionOnNextLossOfFocus;
418 private SelectActionModeCallback.ActionHandler mActionHandler;
420 // Delegate that will handle GET downloads, and be notified of completion of POST downloads.
421 private ContentViewDownloadDelegate mDownloadDelegate;
423 // The AccessibilityInjector that handles loading Accessibility scripts into the web page.
424 private AccessibilityInjector mAccessibilityInjector;
426 // Whether native accessibility, i.e. without any script injection, is allowed.
427 private boolean mNativeAccessibilityAllowed;
429 // Whether native accessibility, i.e. without any script injection, has been enabled.
430 private boolean mNativeAccessibilityEnabled;
432 // Handles native accessibility, i.e. without any script injection.
433 private BrowserAccessibilityManager mBrowserAccessibilityManager;
435 // System accessibility service.
436 private final AccessibilityManager mAccessibilityManager;
438 // Accessibility touch exploration state.
439 private boolean mTouchExplorationEnabled;
441 // Whether accessibility focus should be set to the page when it finishes loading.
442 // This only applies if an accessibility service like TalkBack is running.
443 // This is desirable behavior for a browser window, but not for an embedded
445 private boolean mShouldSetAccessibilityFocusOnPageLoad;
447 // Allows us to dynamically respond when the accessibility script injection flag changes.
448 private ContentObserver mAccessibilityScriptInjectionObserver;
450 // Temporary notification to tell onSizeChanged to focus a form element,
451 // because the OSK was just brought up.
452 private final Rect mFocusPreOSKViewportRect = new Rect();
454 // On tap this will store the x, y coordinates of the touch.
455 private int mLastTapX;
456 private int mLastTapY;
458 // Whether a touch scroll sequence is active, used to hide text selection
459 // handles. Note that a scroll sequence will *always* bound a pinch
460 // sequence, so this will also be true for the duration of a pinch gesture.
461 private boolean mTouchScrollInProgress;
463 // The outstanding fling start events that hasn't got fling end yet. It may be > 1 because
464 // onNativeFlingStopped() is called asynchronously.
465 private int mPotentiallyActiveFlingCount;
467 private ViewAndroid mViewAndroid;
469 private SmartClipDataListener mSmartClipDataListener = null;
471 // This holds the state of editable text (e.g. contents of <input>, contenteditable) of
472 // a focused element.
473 // Every time the user, IME, javascript (Blink), autofill etc. modifies the content, the new
474 // state must be reflected to this to keep consistency.
475 private final Editable mEditable;
478 * PID used to indicate an invalid render process.
480 // Keep in sync with the value returned from ContentViewCoreImpl::GetCurrentRendererProcessId()
481 // if there is no render process.
482 public static final int INVALID_RENDER_PROCESS_PID = 0;
484 // Offsets for the events that passes through this ContentViewCore.
485 private float mCurrentTouchOffsetX;
486 private float mCurrentTouchOffsetY;
488 // Offsets for smart clip
489 private int mSmartClipOffsetX;
490 private int mSmartClipOffsetY;
492 // Whether the ContentViewCore requires the WebContents to be fullscreen in order to lock the
493 // screen orientation.
494 private boolean mFullscreenRequiredForOrientationLock = true;
496 // A ViewAndroidDelegate that delegates to the current container view.
497 private ContentViewAndroidDelegate mViewAndroidDelegate;
500 * Constructs a new ContentViewCore. Embedders must call initialize() after constructing
501 * a ContentViewCore and before using it.
503 * @param context The context used to create this.
505 public ContentViewCore(Context context) {
508 mAdapterInputConnectionFactory = new AdapterInputConnectionFactory();
509 mInputMethodManagerWrapper = new InputMethodManagerWrapper(mContext);
511 mRenderCoordinates = new RenderCoordinates();
512 float deviceScaleFactor = getContext().getResources().getDisplayMetrics().density;
513 String forceScaleFactor = CommandLine.getInstance().getSwitchValue(
514 ContentSwitches.FORCE_DEVICE_SCALE_FACTOR);
515 if (forceScaleFactor != null) {
516 deviceScaleFactor = Float.valueOf(forceScaleFactor);
518 mRenderCoordinates.setDeviceScaleFactor(deviceScaleFactor);
519 mAccessibilityManager = (AccessibilityManager)
520 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
521 mGestureStateListeners = new ObserverList<GestureStateListener>();
522 mGestureStateListenersIterator = mGestureStateListeners.rewindableIterator();
524 mEditable = Editable.Factory.getInstance().newEditable("");
525 Selection.setSelection(mEditable, 0);
529 * @return The context used for creating this ContentViewCore.
532 public Context getContext() {
537 * @return The ViewGroup that all view actions of this ContentViewCore should interact with.
539 public ViewGroup getContainerView() {
540 return mContainerView;
544 * @return The WebContents currently being rendered.
546 public WebContents getWebContents() {
550 /* TODO(aelias): Remove this after downstream callers switch to setTopControlsLayoutHeight. */
551 public void setViewportSizeOffset(int offsetXPix, int offsetYPix) {
552 setTopControlsLayoutHeight(offsetYPix);
556 * Specifies how much smaller the Blink layout size should be relative to the size of this
558 * @param topControlsLayoutHeightPix The Y amount in pixels to shrink the viewport by.
560 public void setTopControlsLayoutHeight(int topControlsLayoutHeightPix) {
561 if (topControlsLayoutHeightPix != mTopControlsLayoutHeightPix) {
562 mTopControlsLayoutHeightPix = topControlsLayoutHeightPix;
563 if (mNativeContentViewCore != 0) nativeWasResized(mNativeContentViewCore);
568 * Returns a delegate that can be used to add and remove views from the current
569 * container view. Clients can safely hold to instances of this class as it handles the
570 * replacement of container views transparently.
572 * NOTE: Use with care, as not all ContentViewCore users setup their container view in the same
573 * way. In particular, the Android WebView has limitations on what implementation details can
574 * be provided via a child view, as they are visible in the API and could introduce
575 * compatibility breaks with existing applications. If in doubt, contact the
576 * android_webview/OWNERS
578 * @return A ViewAndroidDelegate that can be used to add and remove views.
580 public ViewAndroidDelegate getViewAndroidDelegate() {
581 return mViewAndroidDelegate;
585 public void setImeAdapterForTest(ImeAdapter imeAdapter) {
586 mImeAdapter = imeAdapter;
590 public ImeAdapter getImeAdapterForTest() {
595 public void setAdapterInputConnectionFactory(AdapterInputConnectionFactory factory) {
596 mAdapterInputConnectionFactory = factory;
600 public void setInputMethodManagerWrapperForTest(InputMethodManagerWrapper immw) {
601 mInputMethodManagerWrapper = immw;
605 public AdapterInputConnection getInputConnectionForTest() {
606 return mInputConnection;
610 ViewAndroid getViewAndroid() {
614 private ImeAdapter createImeAdapter(Context context) {
615 return new ImeAdapter(mInputMethodManagerWrapper,
616 new ImeAdapter.ImeAdapterDelegate() {
618 public void onImeEvent() {
619 mPopupZoomer.hide(true);
620 getContentViewClient().onImeEvent();
621 if (mFocusedNodeEditable) dismissTextHandles();
625 public void onDismissInput() {
626 getContentViewClient().onImeStateChangeRequested(false);
630 public View getAttachedView() {
631 return mContainerView;
635 public ResultReceiver getNewShowKeyboardReceiver() {
636 return new ResultReceiver(new Handler()) {
638 public void onReceiveResult(int resultCode, Bundle resultData) {
639 getContentViewClient().onImeStateChangeRequested(
640 resultCode == InputMethodManager.RESULT_SHOWN
641 || resultCode == InputMethodManager.RESULT_UNCHANGED_SHOWN);
642 if (resultCode == InputMethodManager.RESULT_SHOWN) {
643 // If OSK is newly shown, delay the form focus until
644 // the onSizeChanged (in order to adjust relative to the
646 // TODO(jdduke): We should not assume that onSizeChanged will
647 // always be called, crbug.com/294908.
648 getContainerView().getWindowVisibleDisplayFrame(
649 mFocusPreOSKViewportRect);
650 } else if (hasFocus() && resultCode
651 == InputMethodManager.RESULT_UNCHANGED_SHOWN) {
652 // If the OSK was already there, focus the form immediately.
653 assert mWebContents != null;
654 mWebContents.scrollFocusedEditableNodeIntoView();
665 * @param containerView The view that will act as a container for all views created by this.
666 * @param internalDispatcher Handles dispatching all hidden or super methods to the
668 * @param nativeWebContents A pointer to the native web contents.
669 * @param windowAndroid An instance of the WindowAndroid.
671 // Perform important post-construction set up of the ContentViewCore.
672 // We do not require the containing view in the constructor to allow embedders to create a
673 // ContentViewCore without having fully created its containing view. The containing view
674 // is a vital component of the ContentViewCore, so embedders must exercise caution in what
675 // they do with the ContentViewCore before calling initialize().
676 // We supply the nativeWebContents pointer here rather than in the constructor to allow us
677 // to set the private browsing mode at a later point for the WebView implementation.
678 // Note that the caller remains the owner of the nativeWebContents and is responsible for
679 // deleting it after destroying the ContentViewCore.
680 public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
681 long nativeWebContents, WindowAndroid windowAndroid) {
682 createContentViewAndroidDelegate();
683 setContainerView(containerView);
684 long windowNativePointer = windowAndroid.getNativePointer();
685 assert windowNativePointer != 0;
686 createViewAndroid(windowAndroid);
688 long viewAndroidNativePointer = mViewAndroid.getNativePointer();
689 assert viewAndroidNativePointer != 0;
691 mZoomControlsDelegate = NO_OP_ZOOM_CONTROLS_DELEGATE;
693 mNativeContentViewCore = nativeInit(
694 nativeWebContents, viewAndroidNativePointer, windowNativePointer,
695 mRetainedJavaScriptObjects);
696 mWebContents = nativeGetWebContentsAndroid(mNativeContentViewCore);
697 mContentSettings = new ContentSettings(this, mNativeContentViewCore);
699 setContainerViewInternals(internalDispatcher);
700 mRenderCoordinates.reset();
701 initPopupZoomer(mContext);
702 mImeAdapter = createImeAdapter(mContext);
705 mAccessibilityInjector = AccessibilityInjector.newInstance(this);
707 mWebContentsObserver = new WebContentsObserver(mWebContents) {
709 public void didFailLoad(boolean isProvisionalLoad, boolean isMainFrame, int errorCode,
710 String description, String failingUrl) {
711 // Navigation that fails the provisional load will have the strong binding removed
712 // here. One for which the provisional load is commited will have the strong binding
713 // removed in navigationEntryCommitted() below.
714 if (isProvisionalLoad) determinedProcessVisibility();
718 public void didNavigateMainFrame(String url, String baseUrl,
719 boolean isNavigationToDifferentPage, boolean isFragmentNavigation) {
720 if (!isNavigationToDifferentPage) return;
721 hidePopupsAndClearSelection();
722 resetScrollInProgress();
723 resetGestureDetection();
727 public void renderProcessGone(boolean wasOomProtected) {
728 hidePopupsAndClearSelection();
729 resetScrollInProgress();
730 // No need to reset gesture detection as the detector will have
731 // been destroyed in the RenderWidgetHostView.
735 public void navigationEntryCommitted() {
736 determinedProcessVisibility();
739 private void determinedProcessVisibility() {
740 // Signal to the process management logic that we can now rely on the process
741 // visibility signal for binding management. Before the navigation commits, its
742 // renderer is considered background even if the pending navigation happens in the
743 // foreground renderer.
744 ChildProcessLauncher.determinedVisibility(getCurrentRenderProcessId());
750 void createContentViewAndroidDelegate() {
751 mViewAndroidDelegate = new ContentViewAndroidDelegate();
755 void createViewAndroid(WindowAndroid windowAndroid) {
756 mViewAndroid = new ViewAndroid(windowAndroid, mViewAndroidDelegate);
760 * Sets a new container view for this {@link ContentViewCore}.
762 * <p>WARNING: This method can also be used to replace the existing container view,
763 * but you should only do it if you have a very good reason to. Replacing the
764 * container view has been designed to support fullscreen in the Webview so it
765 * might not be appropriate for other use cases.
767 * <p>This method only performs a small part of replacing the container view and
768 * embedders are responsible for:
770 * <li>Disconnecting the old container view from this ContentViewCore</li>
771 * <li>Updating the InternalAccessDelegate</li>
772 * <li>Reconciling the state of this ContentViewCore with the new container view</li>
773 * <li>Tearing down and recreating the native GL rendering where appropriate</li>
777 public void setContainerView(ViewGroup containerView) {
779 if (mContainerView != null) {
780 mPastePopupMenu = null;
781 mInputConnection = null;
782 hidePopupsAndClearSelection();
785 mContainerView = containerView;
786 mPositionObserver = new ViewPositionObserver(mContainerView);
787 mContainerView.setClickable(true);
788 mViewAndroidDelegate.updateCurrentContainerView();
793 void onNativeContentViewCoreDestroyed(long nativeContentViewCore) {
794 assert nativeContentViewCore == mNativeContentViewCore;
795 mNativeContentViewCore = 0;
799 * Set the Container view Internals.
800 * @param internalDispatcher Handles dispatching all hidden or super methods to the
803 public void setContainerViewInternals(InternalAccessDelegate internalDispatcher) {
804 mContainerViewInternals = internalDispatcher;
808 void initPopupZoomer(Context context) {
809 mPopupZoomer = new PopupZoomer(context);
810 mPopupZoomer.setOnVisibilityChangedListener(new PopupZoomer.OnVisibilityChangedListener() {
811 // mContainerView can change, but this OnVisibilityChangedListener can only be used
812 // to add and remove views from the mContainerViewAtCreation.
813 private final ViewGroup mContainerViewAtCreation = mContainerView;
816 public void onPopupZoomerShown(final PopupZoomer zoomer) {
817 mContainerViewAtCreation.post(new Runnable() {
820 if (mContainerViewAtCreation.indexOfChild(zoomer) == -1) {
821 mContainerViewAtCreation.addView(zoomer);
828 public void onPopupZoomerHidden(final PopupZoomer zoomer) {
829 mContainerViewAtCreation.post(new Runnable() {
832 if (mContainerViewAtCreation.indexOfChild(zoomer) != -1) {
833 mContainerViewAtCreation.removeView(zoomer);
834 mContainerViewAtCreation.invalidate();
840 // TODO(yongsheng): LONG_TAP is not enabled in PopupZoomer. So need to dispatch a LONG_TAP
841 // gesture if a user completes a tap on PopupZoomer UI after a LONG_PRESS gesture.
842 PopupZoomer.OnTapListener listener = new PopupZoomer.OnTapListener() {
843 // mContainerView can change, but this OnTapListener can only be used
844 // with the mContainerViewAtCreation.
845 private final ViewGroup mContainerViewAtCreation = mContainerView;
848 public boolean onSingleTap(View v, MotionEvent e) {
849 mContainerViewAtCreation.requestFocus();
850 if (mNativeContentViewCore != 0) {
851 nativeSingleTap(mNativeContentViewCore, e.getEventTime(), e.getX(), e.getY());
857 public boolean onLongPress(View v, MotionEvent e) {
858 if (mNativeContentViewCore != 0) {
859 nativeLongPress(mNativeContentViewCore, e.getEventTime(), e.getX(), e.getY());
864 mPopupZoomer.setOnTapListener(listener);
868 public void setPopupZoomerForTest(PopupZoomer popupZoomer) {
869 mPopupZoomer = popupZoomer;
873 * Destroy the internal state of the ContentView. This method may only be
874 * called after the ContentView has been removed from the view system. No
875 * other methods may be called on this ContentView after this method has
878 public void destroy() {
879 if (mNativeContentViewCore != 0) {
880 nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore);
882 mWebContentsObserver.detachFromWebContents();
883 mWebContentsObserver = null;
884 setSmartClipDataListener(null);
885 setZoomControlsDelegate(null);
886 // TODO(igsolla): address TODO in ContentViewClient because ContentViewClient is not
887 // currently a real Null Object.
889 // Instead of deleting the client we use the Null Object pattern to avoid null checks
891 mContentViewClient = new ContentViewClient();
893 if (mViewAndroid != null) mViewAndroid.destroy();
894 mNativeContentViewCore = 0;
895 mContentSettings = null;
896 mJavaScriptInterfaces.clear();
897 mRetainedJavaScriptObjects.clear();
898 unregisterAccessibilityContentObserver();
899 mGestureStateListeners.clear();
900 ScreenOrientationListener.getInstance().removeObserver(this);
901 mPositionObserver.clearListener();
904 private void unregisterAccessibilityContentObserver() {
905 if (mAccessibilityScriptInjectionObserver == null) {
908 getContext().getContentResolver().unregisterContentObserver(
909 mAccessibilityScriptInjectionObserver);
910 mAccessibilityScriptInjectionObserver = null;
914 * Returns true initially, false after destroy() has been called.
915 * It is illegal to call any other public method after destroy().
917 public boolean isAlive() {
918 return mNativeContentViewCore != 0;
922 * This is only useful for passing over JNI to native code that requires ContentViewCore*.
923 * @return native ContentViewCore pointer.
926 public long getNativeContentViewCore() {
927 return mNativeContentViewCore;
930 public void setContentViewClient(ContentViewClient client) {
931 if (client == null) {
932 throw new IllegalArgumentException("The client can't be null.");
934 mContentViewClient = client;
938 public ContentViewClient getContentViewClient() {
939 if (mContentViewClient == null) {
940 // We use the Null Object pattern to avoid having to perform a null check in this class.
941 // We create it lazily because most of the time a client will be set almost immediately
942 // after ContentView is created.
943 mContentViewClient = new ContentViewClient();
944 // We don't set the native ContentViewClient pointer here on purpose. The native
945 // implementation doesn't mind a null delegate and using one is better than passing a
946 // Null Object, since we cut down on the number of JNI calls.
948 return mContentViewClient;
952 private void onBackgroundColorChanged(int color) {
953 getContentViewClient().onBackgroundColorChanged(color);
957 * @return Viewport width in physical pixels as set from onSizeChanged.
960 public int getViewportWidthPix() {
961 return mViewportWidthPix;
965 * @return Viewport height in physical pixels as set from onSizeChanged.
968 public int getViewportHeightPix() {
969 return mViewportHeightPix;
973 * @return Width of underlying physical surface.
976 public int getPhysicalBackingWidthPix() {
977 return mPhysicalBackingWidthPix;
981 * @return Height of underlying physical surface.
984 public int getPhysicalBackingHeightPix() {
985 return mPhysicalBackingHeightPix;
988 /* TODO(aelias): Remove these when downstream callers disappear. */
990 public int getViewportSizeOffsetWidthPix() {
995 public int getViewportSizeOffsetHeightPix() {
996 return getTopControlsLayoutHeightPix();
1000 * @return The amount that the viewport size given to Blink is shrunk by the URL-bar..
1003 public int getTopControlsLayoutHeightPix() {
1004 return mTopControlsLayoutHeightPix;
1008 * @see android.webkit.WebView#getContentHeight()
1010 public float getContentHeightCss() {
1011 return mRenderCoordinates.getContentHeightCss();
1015 * @see android.webkit.WebView#getContentWidth()
1017 public float getContentWidthCss() {
1018 return mRenderCoordinates.getContentWidthCss();
1022 * @return The selected text (empty if no text selected).
1024 public String getSelectedText() {
1025 return mHasSelection ? mLastSelectedText : "";
1029 * @return Whether the current selection is editable (false if no text selected).
1031 public boolean isSelectionEditable() {
1032 return mHasSelection ? mFocusedNodeEditable : false;
1036 * @return Whether the current focused node is editable.
1038 public boolean isFocusedNodeEditable() {
1039 return mFocusedNodeEditable;
1042 // End FrameLayout overrides.
1045 * @see View#onTouchEvent(MotionEvent)
1047 public boolean onTouchEvent(MotionEvent event) {
1048 final boolean isTouchHandleEvent = false;
1049 return onTouchEventImpl(event, isTouchHandleEvent);
1052 private boolean onTouchEventImpl(MotionEvent event, boolean isTouchHandleEvent) {
1053 TraceEvent.begin("onTouchEvent");
1055 int eventAction = event.getActionMasked();
1057 if (eventAction == MotionEvent.ACTION_DOWN) {
1058 cancelRequestToScrollFocusedEditableNodeIntoView();
1061 if (SPenSupport.isSPenSupported(mContext))
1062 eventAction = SPenSupport.convertSPenEventAction(eventAction);
1063 if (!isValidTouchEventActionForNative(eventAction)) return false;
1065 if (mNativeContentViewCore == 0) return false;
1067 // A zero offset is quite common, in which case the unnecessary copy should be avoided.
1068 MotionEvent offset = null;
1069 if (mCurrentTouchOffsetX != 0 || mCurrentTouchOffsetY != 0) {
1070 offset = createOffsetMotionEvent(event);
1074 final int pointerCount = event.getPointerCount();
1075 final boolean consumed = nativeOnTouchEvent(mNativeContentViewCore, event,
1076 event.getEventTime(), eventAction,
1077 pointerCount, event.getHistorySize(), event.getActionIndex(),
1078 event.getX(), event.getY(),
1079 pointerCount > 1 ? event.getX(1) : 0,
1080 pointerCount > 1 ? event.getY(1) : 0,
1081 event.getPointerId(0), pointerCount > 1 ? event.getPointerId(1) : -1,
1082 event.getTouchMajor(), pointerCount > 1 ? event.getTouchMajor(1) : 0,
1083 event.getTouchMinor(), pointerCount > 1 ? event.getTouchMinor(1) : 0,
1084 event.getOrientation(), pointerCount > 1 ? event.getOrientation(1) : 0,
1085 event.getRawX(), event.getRawY(),
1086 event.getToolType(0),
1087 pointerCount > 1 ? event.getToolType(1) : MotionEvent.TOOL_TYPE_UNKNOWN,
1088 event.getButtonState(),
1089 event.getMetaState(),
1090 isTouchHandleEvent);
1092 if (offset != null) offset.recycle();
1095 TraceEvent.end("onTouchEvent");
1099 private static boolean isValidTouchEventActionForNative(int eventAction) {
1100 // Only these actions have any effect on gesture detection. Other
1101 // actions have no corresponding WebTouchEvent type and may confuse the
1102 // touch pipline, so we ignore them entirely.
1103 return eventAction == MotionEvent.ACTION_DOWN
1104 || eventAction == MotionEvent.ACTION_UP
1105 || eventAction == MotionEvent.ACTION_CANCEL
1106 || eventAction == MotionEvent.ACTION_MOVE
1107 || eventAction == MotionEvent.ACTION_POINTER_DOWN
1108 || eventAction == MotionEvent.ACTION_POINTER_UP;
1111 public void setIgnoreRemainingTouchEvents() {
1112 resetGestureDetection();
1115 public boolean isScrollInProgress() {
1116 return mTouchScrollInProgress || mPotentiallyActiveFlingCount > 0;
1119 @SuppressWarnings("unused")
1121 private void onFlingStartEventConsumed(int vx, int vy) {
1122 mTouchScrollInProgress = false;
1123 mPotentiallyActiveFlingCount++;
1124 for (mGestureStateListenersIterator.rewind();
1125 mGestureStateListenersIterator.hasNext();) {
1126 mGestureStateListenersIterator.next().onFlingStartGesture(
1127 vx, vy, computeVerticalScrollOffset(), computeVerticalScrollExtent());
1131 @SuppressWarnings("unused")
1133 private void onFlingStartEventHadNoConsumer(int vx, int vy) {
1134 mTouchScrollInProgress = false;
1135 for (mGestureStateListenersIterator.rewind();
1136 mGestureStateListenersIterator.hasNext();) {
1137 mGestureStateListenersIterator.next().onUnhandledFlingStartEvent(vx, vy);
1141 @SuppressWarnings("unused")
1143 private void onFlingCancelEventAck() {
1144 updateGestureStateListener(GestureEventType.FLING_CANCEL);
1147 @SuppressWarnings("unused")
1149 private void onScrollBeginEventAck() {
1150 mTouchScrollInProgress = true;
1152 mZoomControlsDelegate.invokeZoomPicker();
1153 updateGestureStateListener(GestureEventType.SCROLL_START);
1156 @SuppressWarnings("unused")
1158 private void onScrollUpdateGestureConsumed() {
1159 mZoomControlsDelegate.invokeZoomPicker();
1160 for (mGestureStateListenersIterator.rewind();
1161 mGestureStateListenersIterator.hasNext();) {
1162 mGestureStateListenersIterator.next().onScrollUpdateGestureConsumed();
1166 @SuppressWarnings("unused")
1168 private void onScrollEndEventAck() {
1169 if (!mTouchScrollInProgress) return;
1170 mTouchScrollInProgress = false;
1171 updateGestureStateListener(GestureEventType.SCROLL_END);
1174 @SuppressWarnings("unused")
1176 private void onPinchBeginEventAck() {
1177 updateGestureStateListener(GestureEventType.PINCH_BEGIN);
1180 @SuppressWarnings("unused")
1182 private void onPinchEndEventAck() {
1183 updateGestureStateListener(GestureEventType.PINCH_END);
1186 @SuppressWarnings("unused")
1188 private void onSingleTapEventAck(boolean consumed, int x, int y) {
1189 for (mGestureStateListenersIterator.rewind();
1190 mGestureStateListenersIterator.hasNext();) {
1191 mGestureStateListenersIterator.next().onSingleTap(consumed, x, y);
1196 * Called just prior to a tap or press gesture being forwarded to the renderer.
1198 @SuppressWarnings("unused")
1200 private boolean filterTapOrPressEvent(int type, int x, int y) {
1201 if (type == GestureEventType.LONG_PRESS && offerLongPressToEmbedder()) {
1204 updateForTapOrPress(type, x, y);
1209 public void sendDoubleTapForTest(long timeMs, int x, int y) {
1210 if (mNativeContentViewCore == 0) return;
1211 nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1215 public void flingForTest(long timeMs, int x, int y, int velocityX, int velocityY) {
1216 if (mNativeContentViewCore == 0) return;
1217 nativeFlingCancel(mNativeContentViewCore, timeMs);
1218 nativeScrollBegin(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1219 nativeFlingStart(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1223 * Cancel any fling gestures active.
1224 * @param timeMs Current time (in milliseconds).
1226 public void cancelFling(long timeMs) {
1227 if (mNativeContentViewCore == 0) return;
1228 nativeFlingCancel(mNativeContentViewCore, timeMs);
1232 * Add a listener that gets alerted on gesture state changes.
1233 * @param listener Listener to add.
1235 public void addGestureStateListener(GestureStateListener listener) {
1236 mGestureStateListeners.addObserver(listener);
1240 * Removes a listener that was added to watch for gesture state changes.
1241 * @param listener Listener to remove.
1243 public void removeGestureStateListener(GestureStateListener listener) {
1244 mGestureStateListeners.removeObserver(listener);
1247 void updateGestureStateListener(int gestureType) {
1248 for (mGestureStateListenersIterator.rewind();
1249 mGestureStateListenersIterator.hasNext();) {
1250 GestureStateListener listener = mGestureStateListenersIterator.next();
1251 switch (gestureType) {
1252 case GestureEventType.PINCH_BEGIN:
1253 listener.onPinchStarted();
1255 case GestureEventType.PINCH_END:
1256 listener.onPinchEnded();
1258 case GestureEventType.FLING_END:
1259 listener.onFlingEndGesture(
1260 computeVerticalScrollOffset(),
1261 computeVerticalScrollExtent());
1263 case GestureEventType.FLING_CANCEL:
1264 listener.onFlingCancelGesture();
1266 case GestureEventType.SCROLL_START:
1267 listener.onScrollStarted(
1268 computeVerticalScrollOffset(),
1269 computeVerticalScrollExtent());
1271 case GestureEventType.SCROLL_END:
1272 listener.onScrollEnded(
1273 computeVerticalScrollOffset(),
1274 computeVerticalScrollExtent());
1283 * To be called when the ContentView is shown.
1285 public void onShow() {
1286 assert mWebContents != null;
1287 mWebContents.onShow();
1288 setAccessibilityState(mAccessibilityManager.isEnabled());
1289 restoreSelectionPopupsIfNecessary();
1293 * @return The ID of the renderer process that backs this tab or
1294 * {@link #INVALID_RENDER_PROCESS_PID} if there is none.
1296 public int getCurrentRenderProcessId() {
1297 return nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1301 * To be called when the ContentView is hidden.
1303 public void onHide() {
1304 assert mWebContents != null;
1305 hidePopupsAndPreserveSelection();
1306 setInjectedAccessibility(false);
1307 mWebContents.onHide();
1311 * Return the ContentSettings object used to retrieve the settings for this
1312 * ContentViewCore. For modifications, ChromeNativePreferences is to be used.
1313 * @return A ContentSettings object that can be used to retrieve this
1314 * ContentViewCore's settings.
1316 public ContentSettings getContentSettings() {
1317 return mContentSettings;
1320 private void hidePopupsAndClearSelection() {
1321 mUnselectAllOnActionModeDismiss = true;
1323 // Clear the selection. The selection is cleared on destroying IME
1324 // and also here since we may receive destroy first, for example
1325 // when focus is lost in webview.
1326 clearUserSelection();
1329 private void hidePopupsAndPreserveSelection() {
1330 mUnselectAllOnActionModeDismiss = false;
1334 private void clearUserSelection() {
1335 if (mFocusedNodeEditable) {
1336 if (mInputConnection != null) {
1337 int selectionEnd = Selection.getSelectionEnd(mEditable);
1338 mInputConnection.setSelection(selectionEnd, selectionEnd);
1340 } else if (mImeAdapter != null) {
1341 mImeAdapter.unselect();
1345 private void hidePopups() {
1346 hideSelectActionBar();
1349 mPopupZoomer.hide(false);
1350 if (mUnselectAllOnActionModeDismiss) dismissTextHandles();
1353 private void restoreSelectionPopupsIfNecessary() {
1354 if (mHasSelection && mActionMode == null) showSelectActionBar();
1357 public void hideSelectActionBar() {
1358 if (mActionMode != null) {
1359 mActionMode.finish();
1364 public boolean isSelectActionBarShowing() {
1365 return mActionMode != null;
1368 private void resetGestureDetection() {
1369 if (mNativeContentViewCore == 0) return;
1370 nativeResetGestureDetection(mNativeContentViewCore);
1374 * @see View#onAttachedToWindow()
1376 @SuppressWarnings("javadoc")
1377 public void onAttachedToWindow() {
1378 setAccessibilityState(mAccessibilityManager.isEnabled());
1379 setTextHandlesTemporarilyHidden(false);
1380 restoreSelectionPopupsIfNecessary();
1381 ScreenOrientationListener.getInstance().addObserver(this, mContext);
1382 GamepadList.onAttachedToWindow(mContext);
1386 * @see View#onDetachedFromWindow()
1388 @SuppressWarnings("javadoc")
1389 @SuppressLint("MissingSuperCall")
1390 public void onDetachedFromWindow() {
1391 setInjectedAccessibility(false);
1392 mZoomControlsDelegate.dismissZoomPicker();
1393 unregisterAccessibilityContentObserver();
1395 ScreenOrientationListener.getInstance().removeObserver(this);
1396 GamepadList.onDetachedFromWindow();
1398 // WebView uses PopupWindows for handle rendering, which may remain
1399 // unintentionally visible even after the WebView has been detached.
1400 // Override the handle visibility explicitly to address this, but
1401 // preserve the underlying selection for detachment cases like screen
1402 // locking and app switching.
1403 setTextHandlesTemporarilyHidden(true);
1404 hidePopupsAndPreserveSelection();
1408 * @see View#onVisibilityChanged(android.view.View, int)
1410 public void onVisibilityChanged(View changedView, int visibility) {
1411 if (visibility != View.VISIBLE) {
1412 mZoomControlsDelegate.dismissZoomPicker();
1417 * @see View#onCreateInputConnection(EditorInfo)
1419 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1420 if (!mImeAdapter.hasTextInputType()) {
1421 // Although onCheckIsTextEditor will return false in this case, the EditorInfo
1422 // is still used by the InputMethodService. Need to make sure the IME doesn't
1423 // enter fullscreen mode.
1424 outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
1426 mInputConnection = mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter,
1427 mEditable, outAttrs);
1428 return mInputConnection;
1432 public AdapterInputConnection getAdapterInputConnectionForTest() {
1433 return mInputConnection;
1437 public Editable getEditableForTest() {
1442 * @see View#onCheckIsTextEditor()
1444 public boolean onCheckIsTextEditor() {
1445 return mImeAdapter.hasTextInputType();
1449 * @see View#onConfigurationChanged(Configuration)
1451 @SuppressWarnings("javadoc")
1452 public void onConfigurationChanged(Configuration newConfig) {
1455 if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
1456 if (mNativeContentViewCore != 0) {
1457 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
1458 ImeAdapter.getTextInputTypeNone(), 0 /* no flags */);
1460 mInputMethodManagerWrapper.restartInput(mContainerView);
1462 mContainerViewInternals.super_onConfigurationChanged(newConfig);
1464 // To request layout has side effect, but it seems OK as it only happen in
1465 // onConfigurationChange and layout has to be changed in most case.
1466 mContainerView.requestLayout();
1471 * @see View#onSizeChanged(int, int, int, int)
1473 @SuppressWarnings("javadoc")
1474 public void onSizeChanged(int wPix, int hPix, int owPix, int ohPix) {
1475 if (getViewportWidthPix() == wPix && getViewportHeightPix() == hPix) return;
1477 mViewportWidthPix = wPix;
1478 mViewportHeightPix = hPix;
1479 if (mNativeContentViewCore != 0) {
1480 nativeWasResized(mNativeContentViewCore);
1483 updateAfterSizeChanged();
1487 * Called when the underlying surface the compositor draws to changes size.
1488 * This may be larger than the viewport size.
1490 public void onPhysicalBackingSizeChanged(int wPix, int hPix) {
1491 if (mPhysicalBackingWidthPix == wPix && mPhysicalBackingHeightPix == hPix) return;
1493 mPhysicalBackingWidthPix = wPix;
1494 mPhysicalBackingHeightPix = hPix;
1496 if (mNativeContentViewCore != 0) {
1497 nativeWasResized(mNativeContentViewCore);
1501 /* TODO(aelias): Remove this after downstream callers disappear. */
1502 public void onOverdrawBottomHeightChanged(int overdrawHeightPix) {
1505 private void updateAfterSizeChanged() {
1506 mPopupZoomer.hide(false);
1508 // Execute a delayed form focus operation because the OSK was brought
1510 if (!mFocusPreOSKViewportRect.isEmpty()) {
1511 Rect rect = new Rect();
1512 getContainerView().getWindowVisibleDisplayFrame(rect);
1513 if (!rect.equals(mFocusPreOSKViewportRect)) {
1514 // Only assume the OSK triggered the onSizeChanged if width was preserved.
1515 if (rect.width() == mFocusPreOSKViewportRect.width()) {
1516 assert mWebContents != null;
1517 mWebContents.scrollFocusedEditableNodeIntoView();
1519 cancelRequestToScrollFocusedEditableNodeIntoView();
1524 private void cancelRequestToScrollFocusedEditableNodeIntoView() {
1525 // Zero-ing the rect will prevent |updateAfterSizeChanged()| from
1526 // issuing the delayed form focus event.
1527 mFocusPreOSKViewportRect.setEmpty();
1531 * @see View#onWindowFocusChanged(boolean)
1533 public void onWindowFocusChanged(boolean hasWindowFocus) {
1534 if (!hasWindowFocus) resetGestureDetection();
1537 public void onFocusChanged(boolean gainFocus) {
1539 restoreSelectionPopupsIfNecessary();
1542 cancelRequestToScrollFocusedEditableNodeIntoView();
1543 if (mPreserveSelectionOnNextLossOfFocus) {
1544 mPreserveSelectionOnNextLossOfFocus = false;
1545 hidePopupsAndPreserveSelection();
1547 hidePopupsAndClearSelection();
1550 if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1554 * @see View#onKeyUp(int, KeyEvent)
1556 public boolean onKeyUp(int keyCode, KeyEvent event) {
1557 if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1558 mPopupZoomer.hide(true);
1561 return mContainerViewInternals.super_onKeyUp(keyCode, event);
1565 * @see View#dispatchKeyEventPreIme(KeyEvent)
1567 public boolean dispatchKeyEventPreIme(KeyEvent event) {
1570 return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1577 * @see View#dispatchKeyEvent(KeyEvent)
1579 public boolean dispatchKeyEvent(KeyEvent event) {
1580 if (GamepadList.dispatchKeyEvent(event)) return true;
1581 if (getContentViewClient().shouldOverrideKeyEvent(event)) {
1582 return mContainerViewInternals.super_dispatchKeyEvent(event);
1585 if (mImeAdapter.dispatchKeyEvent(event)) return true;
1587 return mContainerViewInternals.super_dispatchKeyEvent(event);
1591 * @see View#onHoverEvent(MotionEvent)
1592 * Mouse move events are sent on hover enter, hover move and hover exit.
1593 * They are sent on hover exit because sometimes it acts as both a hover
1594 * move and hover exit.
1596 public boolean onHoverEvent(MotionEvent event) {
1597 TraceEvent.begin("onHoverEvent");
1598 MotionEvent offset = createOffsetMotionEvent(event);
1600 if (mBrowserAccessibilityManager != null) {
1601 return mBrowserAccessibilityManager.onHoverEvent(offset);
1604 // Work around Android bug where the x, y coordinates of a hover exit
1605 // event are incorrect when touch exploration is on.
1606 if (mTouchExplorationEnabled && offset.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
1610 mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1611 if (mNativeContentViewCore != 0) {
1612 nativeSendMouseMoveEvent(mNativeContentViewCore, offset.getEventTime(),
1613 offset.getX(), offset.getY());
1618 TraceEvent.end("onHoverEvent");
1623 * @see View#onGenericMotionEvent(MotionEvent)
1625 public boolean onGenericMotionEvent(MotionEvent event) {
1626 if (GamepadList.onGenericMotionEvent(event)) return true;
1627 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1628 switch (event.getAction()) {
1629 case MotionEvent.ACTION_SCROLL:
1630 if (mNativeContentViewCore == 0) return false;
1632 nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
1633 event.getX(), event.getY(),
1634 event.getAxisValue(MotionEvent.AXIS_VSCROLL));
1636 mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1637 // Send a delayed onMouseMove event so that we end
1638 // up hovering over the right position after the scroll.
1639 final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event);
1640 mFakeMouseMoveRunnable = new Runnable() {
1643 onHoverEvent(eventFakeMouseMove);
1644 eventFakeMouseMove.recycle();
1647 mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1651 return mContainerViewInternals.super_onGenericMotionEvent(event);
1655 * Sets the current amount to offset incoming touch events by. This is used to handle content
1656 * moving and not lining up properly with the android input system.
1657 * @param dx The X offset in pixels to shift touch events.
1658 * @param dy The Y offset in pixels to shift touch events.
1660 public void setCurrentMotionEventOffsets(float dx, float dy) {
1661 mCurrentTouchOffsetX = dx;
1662 mCurrentTouchOffsetY = dy;
1665 private MotionEvent createOffsetMotionEvent(MotionEvent src) {
1666 MotionEvent dst = MotionEvent.obtain(src);
1667 dst.offsetLocation(mCurrentTouchOffsetX, mCurrentTouchOffsetY);
1672 * @see View#scrollBy(int, int)
1673 * Currently the ContentView scrolling happens in the native side. In
1674 * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
1675 * are overridden, so that View's mScrollX and mScrollY will be unchanged at
1676 * (0, 0). This is critical for drawing ContentView correctly.
1678 public void scrollBy(int xPix, int yPix) {
1679 if (mNativeContentViewCore != 0) {
1680 nativeScrollBy(mNativeContentViewCore,
1681 SystemClock.uptimeMillis(), 0, 0, xPix, yPix);
1686 * @see View#scrollTo(int, int)
1688 public void scrollTo(int xPix, int yPix) {
1689 if (mNativeContentViewCore == 0) return;
1690 final float xCurrentPix = mRenderCoordinates.getScrollXPix();
1691 final float yCurrentPix = mRenderCoordinates.getScrollYPix();
1692 final float dxPix = xPix - xCurrentPix;
1693 final float dyPix = yPix - yCurrentPix;
1694 if (dxPix != 0 || dyPix != 0) {
1695 long time = SystemClock.uptimeMillis();
1696 nativeScrollBegin(mNativeContentViewCore, time,
1697 xCurrentPix, yCurrentPix, -dxPix, -dyPix);
1698 nativeScrollBy(mNativeContentViewCore,
1699 time, xCurrentPix, yCurrentPix, dxPix, dyPix);
1700 nativeScrollEnd(mNativeContentViewCore, time);
1704 // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1706 public int getNativeScrollXForTest() {
1707 return mRenderCoordinates.getScrollXPixInt();
1710 // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1712 public int getNativeScrollYForTest() {
1713 return mRenderCoordinates.getScrollYPixInt();
1717 * @see View#computeHorizontalScrollExtent()
1719 @SuppressWarnings("javadoc")
1720 public int computeHorizontalScrollExtent() {
1721 return mRenderCoordinates.getLastFrameViewportWidthPixInt();
1725 * @see View#computeHorizontalScrollOffset()
1727 @SuppressWarnings("javadoc")
1728 public int computeHorizontalScrollOffset() {
1729 return mRenderCoordinates.getScrollXPixInt();
1733 * @see View#computeHorizontalScrollRange()
1735 @SuppressWarnings("javadoc")
1736 public int computeHorizontalScrollRange() {
1737 return mRenderCoordinates.getContentWidthPixInt();
1741 * @see View#computeVerticalScrollExtent()
1743 @SuppressWarnings("javadoc")
1744 public int computeVerticalScrollExtent() {
1745 return mRenderCoordinates.getLastFrameViewportHeightPixInt();
1749 * @see View#computeVerticalScrollOffset()
1751 @SuppressWarnings("javadoc")
1752 public int computeVerticalScrollOffset() {
1753 return mRenderCoordinates.getScrollYPixInt();
1757 * @see View#computeVerticalScrollRange()
1759 @SuppressWarnings("javadoc")
1760 public int computeVerticalScrollRange() {
1761 return mRenderCoordinates.getContentHeightPixInt();
1764 // End FrameLayout overrides.
1767 * @see View#awakenScrollBars(int, boolean)
1769 @SuppressWarnings("javadoc")
1770 public boolean awakenScrollBars(int startDelay, boolean invalidate) {
1771 // For the default implementation of ContentView which draws the scrollBars on the native
1772 // side, calling this function may get us into a bad state where we keep drawing the
1773 // scrollBars, so disable it by always returning false.
1774 if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
1777 return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
1781 private void updateForTapOrPress(int type, float xPix, float yPix) {
1782 if (type != GestureEventType.SINGLE_TAP_CONFIRMED
1783 && type != GestureEventType.SINGLE_TAP_UP
1784 && type != GestureEventType.LONG_PRESS
1785 && type != GestureEventType.LONG_TAP) {
1789 if (mContainerView.isFocusable() && mContainerView.isFocusableInTouchMode()
1790 && !mContainerView.isFocused()) {
1791 mContainerView.requestFocus();
1794 if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
1796 mLastTapX = (int) xPix;
1797 mLastTapY = (int) yPix;
1801 * @return The x coordinate for the last point that a tap or press gesture was initiated from.
1803 public int getLastTapX() {
1808 * @return The y coordinate for the last point that a tap or press gesture was initiated from.
1810 public int getLastTapY() {
1814 public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
1815 if (zoomControlsDelegate == null) {
1816 mZoomControlsDelegate = NO_OP_ZOOM_CONTROLS_DELEGATE;
1819 mZoomControlsDelegate = zoomControlsDelegate;
1822 public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
1823 if (mNativeContentViewCore == 0) return;
1824 nativeSetMultiTouchZoomSupportEnabled(mNativeContentViewCore, supportsMultiTouchZoom);
1827 public void updateDoubleTapSupport(boolean supportsDoubleTap) {
1828 if (mNativeContentViewCore == 0) return;
1829 nativeSetDoubleTapSupportEnabled(mNativeContentViewCore, supportsDoubleTap);
1832 public void selectPopupMenuItems(int[] indices) {
1833 if (mNativeContentViewCore != 0) {
1834 nativeSelectPopupMenuItems(mNativeContentViewCore, mNativeSelectPopupSourceFrame,
1837 mNativeSelectPopupSourceFrame = 0;
1838 mSelectPopup = null;
1842 * Send the screen orientation value to the renderer.
1845 void sendOrientationChangeEvent(int orientation) {
1846 if (mNativeContentViewCore == 0) return;
1848 nativeSendOrientationChangeEvent(mNativeContentViewCore, orientation);
1852 * Register the delegate to be used when content can not be handled by
1853 * the rendering engine, and should be downloaded instead. This will replace
1854 * the current delegate, if any.
1855 * @param delegate An implementation of ContentViewDownloadDelegate.
1857 public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
1858 mDownloadDelegate = delegate;
1861 // Called by DownloadController.
1862 ContentViewDownloadDelegate getDownloadDelegate() {
1863 return mDownloadDelegate;
1867 public SelectActionModeCallback.ActionHandler getSelectActionHandler() {
1868 return mActionHandler;
1871 private void showSelectActionBar() {
1872 if (mActionMode != null) {
1873 mActionMode.invalidate();
1877 // Start a new action mode with a SelectActionModeCallback.
1878 if (mActionHandler == null) {
1879 mActionHandler = new SelectActionModeCallback.ActionHandler() {
1881 public void selectAll() {
1882 mImeAdapter.selectAll();
1891 public void copy() {
1896 public void paste() {
1897 mImeAdapter.paste();
1901 public void share() {
1902 final String query = getSelectedText();
1903 if (TextUtils.isEmpty(query)) return;
1905 Intent send = new Intent(Intent.ACTION_SEND);
1906 send.setType("text/plain");
1907 send.putExtra(Intent.EXTRA_TEXT, query);
1909 Intent i = Intent.createChooser(send, getContext().getString(
1910 R.string.actionbar_share));
1911 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1912 getContext().startActivity(i);
1913 } catch (android.content.ActivityNotFoundException ex) {
1914 // If no app handles it, do nothing.
1919 public void search() {
1920 final String query = getSelectedText();
1921 if (TextUtils.isEmpty(query)) return;
1923 // See if ContentViewClient wants to override
1924 if (getContentViewClient().doesPerformWebSearch()) {
1925 getContentViewClient().performWebSearch(query);
1929 Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
1930 i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
1931 i.putExtra(SearchManager.QUERY, query);
1932 i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
1933 if (!(getContext() instanceof Activity)) {
1934 i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1937 getContext().startActivity(i);
1938 } catch (android.content.ActivityNotFoundException ex) {
1939 // If no app handles it, do nothing.
1944 public boolean isSelectionPassword() {
1945 return mImeAdapter.isSelectionPassword();
1949 public boolean isSelectionEditable() {
1950 return mFocusedNodeEditable;
1954 public void onDestroyActionMode() {
1956 if (mUnselectAllOnActionModeDismiss) {
1957 dismissTextHandles();
1958 clearUserSelection();
1960 getContentViewClient().onContextualActionBarHidden();
1964 public boolean isShareAvailable() {
1965 Intent intent = new Intent(Intent.ACTION_SEND);
1966 intent.setType("text/plain");
1967 return getContext().getPackageManager().queryIntentActivities(intent,
1968 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
1972 public boolean isWebSearchAvailable() {
1973 if (getContentViewClient().doesPerformWebSearch()) return true;
1974 Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
1975 intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
1976 return getContext().getPackageManager().queryIntentActivities(intent,
1977 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
1982 // On ICS, startActionMode throws an NPE when getParent() is null.
1983 if (mContainerView.getParent() != null) {
1984 assert mWebContents != null;
1985 mActionMode = mContainerView.startActionMode(
1986 getContentViewClient().getSelectActionModeCallback(getContext(), mActionHandler,
1987 mWebContents.isIncognito()));
1989 mUnselectAllOnActionModeDismiss = true;
1990 if (mActionMode == null) {
1991 // There is no ActionMode, so remove the selection.
1992 mImeAdapter.unselect();
1994 getContentViewClient().onContextualActionBarShown();
1999 * Clears the current text selection.
2001 public void clearSelection() {
2002 mImeAdapter.unselect();
2006 * Ensure the selection is preserved the next time the view loses focus.
2008 public void preserveSelectionOnNextLossOfFocus() {
2009 mPreserveSelectionOnNextLossOfFocus = true;
2013 * @return Whether the page has an active, touch-controlled selection region.
2016 public boolean hasSelection() {
2017 return mHasSelection;
2021 * @return Whether the page has an active, touch-controlled insertion handle.
2024 protected boolean hasInsertion() {
2025 return mHasInsertion;
2028 private void hidePastePopup() {
2029 if (mPastePopupMenu == null) return;
2030 mPastePopupMenu.hide();
2034 private void onSelectionEvent(int eventType, float posXDip, float posYDip) {
2035 switch (eventType) {
2036 case SelectionEventType.SELECTION_SHOWN:
2037 mHasSelection = true;
2038 mUnselectAllOnActionModeDismiss = true;
2039 // TODO(cjhopman): Remove this when there is a better signal that long press caused
2040 // a selection. See http://crbug.com/150151.
2041 mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2042 showSelectActionBar();
2045 case SelectionEventType.SELECTION_CLEARED:
2046 mHasSelection = false;
2047 mUnselectAllOnActionModeDismiss = false;
2048 hideSelectActionBar();
2051 case SelectionEventType.SELECTION_DRAG_STARTED:
2054 case SelectionEventType.SELECTION_DRAG_STOPPED:
2057 case SelectionEventType.INSERTION_SHOWN:
2058 mHasInsertion = true;
2061 case SelectionEventType.INSERTION_MOVED:
2062 if (mPastePopupMenu == null) break;
2063 if (!isScrollInProgress() && mPastePopupMenu.isShowing()) {
2064 showPastePopup((int) posXDip, (int) posYDip);
2070 case SelectionEventType.INSERTION_TAPPED:
2071 if (mWasPastePopupShowingOnInsertionDragStart)
2074 showPastePopup((int) posXDip, (int) posYDip);
2077 case SelectionEventType.INSERTION_CLEARED:
2078 mHasInsertion = false;
2082 case SelectionEventType.INSERTION_DRAG_STARTED:
2083 mWasPastePopupShowingOnInsertionDragStart =
2084 mPastePopupMenu != null && mPastePopupMenu.isShowing();
2089 assert false : "Invalid selection event type.";
2092 final float scale = mRenderCoordinates.getDeviceScaleFactor();
2093 getContentViewClient().onSelectionEvent(eventType, posXDip * scale, posYDip * scale);
2096 private void dismissTextHandles() {
2097 mHasSelection = false;
2098 mHasInsertion = false;
2099 if (mNativeContentViewCore != 0) nativeDismissTextHandles(mNativeContentViewCore);
2102 private void setTextHandlesTemporarilyHidden(boolean hide) {
2103 if (mNativeContentViewCore == 0) return;
2104 nativeSetTextHandlesTemporarilyHidden(mNativeContentViewCore, hide);
2108 * Hides the IME if the containerView is the active view for IME.
2110 public void hideImeIfNeeded() {
2111 // Hide input method window from the current view synchronously
2112 // because ImeAdapter does so asynchronouly with a delay, and
2113 // by the time when ImeAdapter dismisses the input, the
2114 // containerView may have lost focus.
2115 // We cannot trust ContentViewClient#onImeStateChangeRequested to
2116 // hide the input window because it has an empty default implementation.
2117 // So we need to explicitly hide the input method window here.
2118 if (mInputMethodManagerWrapper.isActive(mContainerView)) {
2119 mInputMethodManagerWrapper.hideSoftInputFromWindow(
2120 mContainerView.getWindowToken(), 0, null);
2122 getContentViewClient().onImeStateChangeRequested(false);
2125 @SuppressWarnings("unused")
2127 private void updateFrameInfo(
2128 float scrollOffsetX, float scrollOffsetY,
2129 float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
2130 float contentWidth, float contentHeight,
2131 float viewportWidth, float viewportHeight,
2132 float controlsOffsetYCss, float contentOffsetYCss) {
2133 TraceEvent.begin("ContentViewCore:updateFrameInfo");
2134 // Adjust contentWidth/Height to be always at least as big as
2135 // the actual viewport (as set by onSizeChanged).
2136 final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2137 contentWidth = Math.max(contentWidth,
2138 mViewportWidthPix / (deviceScale * pageScaleFactor));
2139 contentHeight = Math.max(contentHeight,
2140 mViewportHeightPix / (deviceScale * pageScaleFactor));
2141 final float contentOffsetYPix = mRenderCoordinates.fromDipToPix(contentOffsetYCss);
2143 final boolean contentSizeChanged =
2144 contentWidth != mRenderCoordinates.getContentWidthCss()
2145 || contentHeight != mRenderCoordinates.getContentHeightCss();
2146 final boolean scaleLimitsChanged =
2147 minPageScaleFactor != mRenderCoordinates.getMinPageScaleFactor()
2148 || maxPageScaleFactor != mRenderCoordinates.getMaxPageScaleFactor();
2149 final boolean pageScaleChanged =
2150 pageScaleFactor != mRenderCoordinates.getPageScaleFactor();
2151 final boolean scrollChanged =
2153 || scrollOffsetX != mRenderCoordinates.getScrollX()
2154 || scrollOffsetY != mRenderCoordinates.getScrollY();
2155 final boolean contentOffsetChanged =
2156 contentOffsetYPix != mRenderCoordinates.getContentOffsetYPix();
2158 final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
2159 final boolean needUpdateZoomControls = scaleLimitsChanged || scrollChanged;
2161 if (needHidePopupZoomer) mPopupZoomer.hide(true);
2163 if (scrollChanged) {
2164 mContainerViewInternals.onScrollChanged(
2165 (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetX),
2166 (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetY),
2167 (int) mRenderCoordinates.getScrollXPix(),
2168 (int) mRenderCoordinates.getScrollYPix());
2171 mRenderCoordinates.updateFrameInfo(
2172 scrollOffsetX, scrollOffsetY,
2173 contentWidth, contentHeight,
2174 viewportWidth, viewportHeight,
2175 pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
2178 if (scrollChanged || contentOffsetChanged) {
2179 for (mGestureStateListenersIterator.rewind();
2180 mGestureStateListenersIterator.hasNext();) {
2181 mGestureStateListenersIterator.next().onScrollOffsetOrExtentChanged(
2182 computeVerticalScrollOffset(),
2183 computeVerticalScrollExtent());
2187 if (needUpdateZoomControls) mZoomControlsDelegate.updateZoomControls();
2189 // Update offsets for fullscreen.
2190 final float controlsOffsetPix = controlsOffsetYCss * deviceScale;
2191 // TODO(aelias): Remove last argument after downstream removes it.
2192 getContentViewClient().onOffsetsForFullscreenChanged(
2193 controlsOffsetPix, contentOffsetYPix, 0);
2195 if (mBrowserAccessibilityManager != null) {
2196 mBrowserAccessibilityManager.notifyFrameInfoInitialized();
2198 TraceEvent.end("ContentViewCore:updateFrameInfo");
2202 private void updateImeAdapter(long nativeImeAdapterAndroid, int textInputType,
2203 int textInputFlags, String text, int selectionStart, int selectionEnd,
2204 int compositionStart, int compositionEnd, boolean showImeIfNeeded,
2205 boolean isNonImeChange) {
2207 mFocusedNodeEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
2208 if (!mFocusedNodeEditable) hidePastePopup();
2210 mImeAdapter.updateKeyboardVisibility(
2211 nativeImeAdapterAndroid, textInputType, textInputFlags, showImeIfNeeded);
2213 if (mInputConnection != null) {
2214 mInputConnection.updateState(text, selectionStart, selectionEnd, compositionStart,
2215 compositionEnd, isNonImeChange);
2218 if (mActionMode != null) mActionMode.invalidate();
2222 @SuppressWarnings("unused")
2224 private void setTitle(String title) {
2225 getContentViewClient().onUpdateTitle(title);
2229 * Called (from native) when the <select> popup needs to be shown.
2230 * @param nativeSelectPopupSourceFrame The native RenderFrameHost that owns the popup.
2231 * @param items Items to show.
2232 * @param enabled POPUP_ITEM_TYPEs for items.
2233 * @param multiple Whether the popup menu should support multi-select.
2234 * @param selectedIndices Indices of selected items.
2236 @SuppressWarnings("unused")
2238 private void showSelectPopup(long nativeSelectPopupSourceFrame, Rect bounds, String[] items,
2239 int[] enabled, boolean multiple, int[] selectedIndices) {
2240 if (mContainerView.getParent() == null || mContainerView.getVisibility() != View.VISIBLE) {
2241 mNativeSelectPopupSourceFrame = nativeSelectPopupSourceFrame;
2242 selectPopupMenuItems(null);
2246 hidePopupsAndClearSelection();
2247 assert mNativeSelectPopupSourceFrame == 0 : "Zombie popup did not clear the frame source";
2249 assert items.length == enabled.length;
2250 List<SelectPopupItem> popupItems = new ArrayList<SelectPopupItem>();
2251 for (int i = 0; i < items.length; i++) {
2252 popupItems.add(new SelectPopupItem(items[i], enabled[i]));
2254 if (DeviceFormFactor.isTablet(mContext) && !multiple && !isTouchExplorationEnabled()) {
2255 mSelectPopup = new SelectPopupDropdown(this, popupItems, bounds, selectedIndices);
2257 mSelectPopup = new SelectPopupDialog(this, popupItems, multiple, selectedIndices);
2259 mNativeSelectPopupSourceFrame = nativeSelectPopupSourceFrame;
2260 mSelectPopup.show();
2264 * Called when the <select> popup needs to be hidden.
2267 private void hideSelectPopup() {
2268 if (mSelectPopup != null) mSelectPopup.hide();
2272 * @return The visible select popup being shown.
2274 public SelectPopup getSelectPopupForTest() {
2275 return mSelectPopup;
2278 @SuppressWarnings("unused")
2280 private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
2281 mPopupZoomer.setBitmap(zoomedBitmap);
2282 mPopupZoomer.show(targetRect);
2285 @SuppressWarnings("unused")
2287 private TouchEventSynthesizer createTouchEventSynthesizer() {
2288 return new TouchEventSynthesizer(this);
2291 @SuppressWarnings("unused")
2293 private PopupTouchHandleDrawable createPopupTouchHandleDrawable() {
2294 if (mTouchHandleDelegate == null) {
2295 mTouchHandleDelegate = new PopupTouchHandleDrawableDelegate() {
2297 public View getParent() {
2298 return getContainerView();
2302 public PositionObserver getParentPositionObserver() {
2303 return mPositionObserver;
2307 public boolean onTouchHandleEvent(MotionEvent event) {
2308 final boolean isTouchHandleEvent = true;
2309 return onTouchEventImpl(event, isTouchHandleEvent);
2313 public boolean isScrollInProgress() {
2314 return ContentViewCore.this.isScrollInProgress();
2318 return new PopupTouchHandleDrawable(mTouchHandleDelegate);
2321 @SuppressWarnings("unused")
2323 private void onSelectionChanged(String text) {
2324 mLastSelectedText = text;
2325 getContentViewClient().onSelectionChanged(text);
2328 @SuppressWarnings("unused")
2330 private void showPastePopupWithFeedback(int xDip, int yDip) {
2331 // TODO(jdduke): Remove this when there is a better signal that long press caused
2332 // showing of the paste popup. See http://crbug.com/150151.
2333 if (showPastePopup(xDip, yDip)) {
2334 mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2338 private boolean showPastePopup(int xDip, int yDip) {
2339 if (!mHasInsertion || !canPaste()) return false;
2340 final float contentOffsetYPix = mRenderCoordinates.getContentOffsetYPix();
2341 getPastePopup().showAt(
2342 (int) mRenderCoordinates.fromDipToPix(xDip),
2343 (int) (mRenderCoordinates.fromDipToPix(yDip) + contentOffsetYPix));
2347 private PastePopupMenu getPastePopup() {
2348 if (mPastePopupMenu == null) {
2349 mPastePopupMenu = new PastePopupMenu(getContainerView(),
2350 new PastePopupMenuDelegate() {
2352 public void paste() {
2353 mImeAdapter.paste();
2354 dismissTextHandles();
2358 return mPastePopupMenu;
2362 public PastePopupMenu getPastePopupForTest() {
2363 return getPastePopup();
2366 private boolean canPaste() {
2367 if (!mFocusedNodeEditable) return false;
2368 return ((ClipboardManager) mContext.getSystemService(
2369 Context.CLIPBOARD_SERVICE)).hasPrimaryClip();
2372 @SuppressWarnings("unused")
2374 private void onRenderProcessChange() {
2379 * Attaches the native ImeAdapter object to the java ImeAdapter to allow communication via JNI.
2381 public void attachImeAdapter() {
2382 if (mImeAdapter != null && mNativeContentViewCore != 0) {
2383 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2388 * @see View#hasFocus()
2391 private boolean hasFocus() {
2392 // If the container view is not focusable, we consider it always focused from
2393 // Chromium's point of view.
2394 if (!mContainerView.isFocusable()) return true;
2395 return mContainerView.hasFocus();
2399 * Checks whether the ContentViewCore can be zoomed in.
2401 * @return True if the ContentViewCore can be zoomed in.
2403 // This method uses the term 'zoom' for legacy reasons, but relates
2404 // to what chrome calls the 'page scale factor'.
2405 public boolean canZoomIn() {
2406 final float zoomInExtent = mRenderCoordinates.getMaxPageScaleFactor()
2407 - mRenderCoordinates.getPageScaleFactor();
2408 return zoomInExtent > ZOOM_CONTROLS_EPSILON;
2412 * Checks whether the ContentViewCore can be zoomed out.
2414 * @return True if the ContentViewCore can be zoomed out.
2416 // This method uses the term 'zoom' for legacy reasons, but relates
2417 // to what chrome calls the 'page scale factor'.
2418 public boolean canZoomOut() {
2419 final float zoomOutExtent = mRenderCoordinates.getPageScaleFactor()
2420 - mRenderCoordinates.getMinPageScaleFactor();
2421 return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
2425 * Zooms in the ContentViewCore by 25% (or less if that would result in
2426 * zooming in more than possible).
2428 * @return True if there was a zoom change, false otherwise.
2430 // This method uses the term 'zoom' for legacy reasons, but relates
2431 // to what chrome calls the 'page scale factor'.
2432 public boolean zoomIn() {
2436 return pinchByDelta(1.25f);
2440 * Zooms out the ContentViewCore by 20% (or less if that would result in
2441 * zooming out more than possible).
2443 * @return True if there was a zoom change, false otherwise.
2445 // This method uses the term 'zoom' for legacy reasons, but relates
2446 // to what chrome calls the 'page scale factor'.
2447 public boolean zoomOut() {
2448 if (!canZoomOut()) {
2451 return pinchByDelta(0.8f);
2455 * Resets the zoom factor of the ContentViewCore.
2457 * @return True if there was a zoom change, false otherwise.
2459 // This method uses the term 'zoom' for legacy reasons, but relates
2460 // to what chrome calls the 'page scale factor'.
2461 public boolean zoomReset() {
2462 // The page scale factor is initialized to mNativeMinimumScale when
2463 // the page finishes loading. Thus sets it back to mNativeMinimumScale.
2464 if (!canZoomOut()) return false;
2465 return pinchByDelta(
2466 mRenderCoordinates.getMinPageScaleFactor()
2467 / mRenderCoordinates.getPageScaleFactor());
2471 * Simulate a pinch zoom gesture.
2473 * @param delta the factor by which the current page scale should be multiplied by.
2474 * @return whether the gesture was sent.
2476 public boolean pinchByDelta(float delta) {
2477 if (mNativeContentViewCore == 0) return false;
2479 long timeMs = SystemClock.uptimeMillis();
2480 int xPix = getViewportWidthPix() / 2;
2481 int yPix = getViewportHeightPix() / 2;
2483 nativePinchBegin(mNativeContentViewCore, timeMs, xPix, yPix);
2484 nativePinchBy(mNativeContentViewCore, timeMs, xPix, yPix, delta);
2485 nativePinchEnd(mNativeContentViewCore, timeMs);
2491 * Invokes the graphical zoom picker widget for this ContentView.
2493 public void invokeZoomPicker() {
2494 mZoomControlsDelegate.invokeZoomPicker();
2498 * Enables or disables inspection of JavaScript objects added via
2499 * {@link #addJavascriptInterface(Object, String)} by means of Object.keys() method and
2500 * "for .. in" loop. Being able to inspect JavaScript objects is useful
2501 * when debugging hybrid Android apps, but can't be enabled for legacy applications due
2502 * to compatibility risks.
2504 * @param allow Whether to allow JavaScript objects inspection.
2506 public void setAllowJavascriptInterfacesInspection(boolean allow) {
2507 nativeSetAllowJavascriptInterfacesInspection(mNativeContentViewCore, allow);
2511 * Returns JavaScript interface objects previously injected via
2512 * {@link #addJavascriptInterface(Object, String)}.
2514 * @return the mapping of names to interface objects and corresponding annotation classes
2516 public Map<String, Pair<Object, Class>> getJavascriptInterfaces() {
2517 return mJavaScriptInterfaces;
2521 * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
2522 * and automatically pass in {@link JavascriptInterface} as the required annotation.
2524 * @param object The Java object to inject into the ContentViewCore's JavaScript context. Null
2525 * values are ignored.
2526 * @param name The name used to expose the instance in JavaScript.
2528 public void addJavascriptInterface(Object object, String name) {
2529 addPossiblyUnsafeJavascriptInterface(object, name, JavascriptInterface.class);
2533 * This method injects the supplied Java object into the ContentViewCore.
2534 * The object is injected into the JavaScript context of the main frame,
2535 * using the supplied name. This allows the Java object to be accessed from
2536 * JavaScript. Note that that injected objects will not appear in
2537 * JavaScript until the page is next (re)loaded. For example:
2538 * <pre> view.addJavascriptInterface(new Object(), "injectedObject");
2539 * view.loadData("<!DOCTYPE html><title></title>", "text/html", null);
2540 * view.loadUrl("javascript:alert(injectedObject.toString())");</pre>
2541 * <p><strong>IMPORTANT:</strong>
2543 * <li> addJavascriptInterface() can be used to allow JavaScript to control
2544 * the host application. This is a powerful feature, but also presents a
2545 * security risk. Use of this method in a ContentViewCore containing
2546 * untrusted content could allow an attacker to manipulate the host
2547 * application in unintended ways, executing Java code with the permissions
2548 * of the host application. Use extreme care when using this method in a
2549 * ContentViewCore which could contain untrusted content. Particular care
2550 * should be taken to avoid unintentional access to inherited methods, such
2551 * as {@link Object#getClass()}. To prevent access to inherited methods,
2552 * pass an annotation for {@code requiredAnnotation}. This will ensure
2553 * that only methods with {@code requiredAnnotation} are exposed to the
2554 * Javascript layer. {@code requiredAnnotation} will be passed to all
2555 * subsequently injected Java objects if any methods return an object. This
2556 * means the same restrictions (or lack thereof) will apply. Alternatively,
2557 * {@link #addJavascriptInterface(Object, String)} can be called, which
2558 * automatically uses the {@link JavascriptInterface} annotation.
2559 * <li> JavaScript interacts with Java objects on a private, background
2560 * thread of the ContentViewCore. Care is therefore required to maintain
2561 * thread safety.</li>
2564 * @param object The Java object to inject into the
2565 * ContentViewCore's JavaScript context. Null
2566 * values are ignored.
2567 * @param name The name used to expose the instance in
2569 * @param requiredAnnotation Restrict exposed methods to ones with this
2570 * annotation. If {@code null} all methods are
2574 public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
2575 Class<? extends Annotation> requiredAnnotation) {
2576 if (mNativeContentViewCore != 0 && object != null) {
2577 mJavaScriptInterfaces.put(name, new Pair<Object, Class>(object, requiredAnnotation));
2578 nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation);
2583 * Removes a previously added JavaScript interface with the given name.
2585 * @param name The name of the interface to remove.
2587 public void removeJavascriptInterface(String name) {
2588 mJavaScriptInterfaces.remove(name);
2589 if (mNativeContentViewCore != 0) {
2590 nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2595 * Return the current scale of the ContentView.
2596 * @return The current page scale factor.
2599 public float getScale() {
2600 return mRenderCoordinates.getPageScaleFactor();
2604 private void startContentIntent(String contentUrl) {
2605 getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2609 public void onAccessibilityStateChanged(boolean enabled) {
2610 setAccessibilityState(enabled);
2614 * Determines whether or not this ContentViewCore can handle this accessibility action.
2615 * @param action The action to perform.
2616 * @return Whether or not this action is supported.
2618 public boolean supportsAccessibilityAction(int action) {
2619 return mAccessibilityInjector.supportsAccessibilityAction(action);
2623 * Attempts to perform an accessibility action on the web content. If the accessibility action
2624 * cannot be processed, it returns {@code null}, allowing the caller to know to call the
2625 * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value.
2626 * Otherwise the return value from this method should be used.
2627 * @param action The action to perform.
2628 * @param arguments Optional action arguments.
2629 * @return Whether the action was performed or {@code null} if the call should be delegated to
2630 * the super {@link View} class.
2632 public boolean performAccessibilityAction(int action, Bundle arguments) {
2633 if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2634 return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2641 * Set the BrowserAccessibilityManager, used for native accessibility
2642 * (not script injection). This is only set when system accessibility
2644 * @param manager The new BrowserAccessibilityManager.
2646 public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
2647 mBrowserAccessibilityManager = manager;
2651 * Get the BrowserAccessibilityManager, used for native accessibility
2652 * (not script injection). This will return null when system accessibility
2654 * @return This view's BrowserAccessibilityManager.
2656 public BrowserAccessibilityManager getBrowserAccessibilityManager() {
2657 return mBrowserAccessibilityManager;
2661 * If native accessibility (not script injection) is enabled, and if this is
2662 * running on JellyBean or later, returns an AccessibilityNodeProvider that
2663 * implements native accessibility for this view. Returns null otherwise.
2664 * Lazily initializes native accessibility here if it's allowed.
2665 * @return The AccessibilityNodeProvider, if available, or null otherwise.
2667 public AccessibilityNodeProvider getAccessibilityNodeProvider() {
2668 if (mBrowserAccessibilityManager != null) {
2669 return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
2672 if (mNativeAccessibilityAllowed && !mNativeAccessibilityEnabled
2673 && mNativeContentViewCore != 0
2674 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
2675 mNativeAccessibilityEnabled = true;
2676 nativeSetAccessibilityEnabled(mNativeContentViewCore, true);
2683 * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
2685 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2686 // Note: this is only used by the script-injecting accessibility code.
2687 mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
2691 * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
2693 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
2694 // Note: this is only used by the script-injecting accessibility code.
2695 event.setClassName(this.getClass().getName());
2697 // Identify where the top-left of the screen currently points to.
2698 event.setScrollX(mRenderCoordinates.getScrollXPixInt());
2699 event.setScrollY(mRenderCoordinates.getScrollYPixInt());
2701 // The maximum scroll values are determined by taking the content dimensions and
2702 // subtracting off the actual dimensions of the ChromeView.
2703 int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt());
2704 int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt());
2705 event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0);
2707 // Setting the maximum scroll values requires API level 15 or higher.
2708 final int sdkVersionRequiredToSetScroll = 15;
2709 if (Build.VERSION.SDK_INT >= sdkVersionRequiredToSetScroll) {
2710 event.setMaxScrollX(maxScrollXPix);
2711 event.setMaxScrollY(maxScrollYPix);
2716 * Returns whether accessibility script injection is enabled on the device
2718 public boolean isDeviceAccessibilityScriptInjectionEnabled() {
2720 // On JellyBean and higher, native accessibility is the default so script
2721 // injection is only allowed if enabled via a flag.
2722 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
2723 && !CommandLine.getInstance().hasSwitch(
2724 ContentSwitches.ENABLE_ACCESSIBILITY_SCRIPT_INJECTION)) {
2728 if (!mContentSettings.getJavaScriptEnabled()) {
2732 int result = getContext().checkCallingOrSelfPermission(
2733 android.Manifest.permission.INTERNET);
2734 if (result != PackageManager.PERMISSION_GRANTED) {
2738 Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
2739 field.setAccessible(true);
2740 String accessibilityScriptInjection = (String) field.get(null);
2741 ContentResolver contentResolver = getContext().getContentResolver();
2743 if (mAccessibilityScriptInjectionObserver == null) {
2744 ContentObserver contentObserver = new ContentObserver(new Handler()) {
2746 public void onChange(boolean selfChange, Uri uri) {
2747 setAccessibilityState(mAccessibilityManager.isEnabled());
2750 contentResolver.registerContentObserver(
2751 Settings.Secure.getUriFor(accessibilityScriptInjection),
2754 mAccessibilityScriptInjectionObserver = contentObserver;
2757 return Settings.Secure.getInt(contentResolver, accessibilityScriptInjection, 0) == 1;
2758 } catch (NoSuchFieldException e) {
2759 // Do nothing, default to false.
2760 } catch (IllegalAccessException e) {
2761 // Do nothing, default to false.
2767 * Returns whether or not accessibility injection is being used.
2769 public boolean isInjectingAccessibilityScript() {
2770 return mAccessibilityInjector.accessibilityIsAvailable();
2774 * Returns true if accessibility is on and touch exploration is enabled.
2776 public boolean isTouchExplorationEnabled() {
2777 return mTouchExplorationEnabled;
2781 * Turns browser accessibility on or off.
2782 * If |state| is |false|, this turns off both native and injected accessibility.
2783 * Otherwise, if accessibility script injection is enabled, this will enable the injected
2784 * accessibility scripts. Native accessibility is enabled on demand.
2786 public void setAccessibilityState(boolean state) {
2788 setInjectedAccessibility(false);
2789 mNativeAccessibilityAllowed = false;
2790 mTouchExplorationEnabled = false;
2792 boolean useScriptInjection = isDeviceAccessibilityScriptInjectionEnabled();
2793 setInjectedAccessibility(useScriptInjection);
2794 mNativeAccessibilityAllowed = !useScriptInjection;
2795 mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
2800 * Enable or disable injected accessibility features
2802 public void setInjectedAccessibility(boolean enabled) {
2803 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
2804 mAccessibilityInjector.setScriptEnabled(enabled);
2808 * Stop any TTS notifications that are currently going on.
2810 public void stopCurrentAccessibilityNotifications() {
2811 mAccessibilityInjector.onPageLostFocus();
2815 * Return whether or not we should set accessibility focus on page load.
2817 public boolean shouldSetAccessibilityFocusOnPageLoad() {
2818 return mShouldSetAccessibilityFocusOnPageLoad;
2822 * Sets whether or not we should set accessibility focus on page load.
2823 * This only applies if an accessibility service like TalkBack is running.
2824 * This is desirable behavior for a browser window, but not for an embedded
2827 public void setShouldSetAccessibilityFocusOnPageLoad(boolean on) {
2828 mShouldSetAccessibilityFocusOnPageLoad = on;
2833 * @return The cached copy of render positions and scales.
2835 public RenderCoordinates getRenderCoordinates() {
2836 return mRenderCoordinates;
2840 private static Rect createRect(int x, int y, int right, int bottom) {
2841 return new Rect(x, y, right, bottom);
2844 public void extractSmartClipData(int x, int y, int width, int height) {
2845 if (mNativeContentViewCore != 0) {
2846 x += mSmartClipOffsetX;
2847 y += mSmartClipOffsetY;
2848 nativeExtractSmartClipData(mNativeContentViewCore, x, y, width, height);
2853 * Set offsets for smart clip.
2855 * <p>This should be called if there is a viewport change introduced by,
2856 * e.g., show and hide of a location bar.
2858 * @param offsetX Offset for X position.
2859 * @param offsetY Offset for Y position.
2861 public void setSmartClipOffsets(int offsetX, int offsetY) {
2862 mSmartClipOffsetX = offsetX;
2863 mSmartClipOffsetY = offsetY;
2867 private void onSmartClipDataExtracted(String text, String html, Rect clipRect) {
2868 // Translate the positions by the offsets introduced by location bar. Note that the
2869 // coordinates are in dp scale, and that this definitely has the potential to be
2870 // different from the offsets when extractSmartClipData() was called. However,
2871 // as long as OEM has a UI that consumes all the inputs and waits until the
2872 // callback is called, then there shouldn't be any difference.
2873 // TODO(changwan): once crbug.com/416432 is resolved, try to pass offsets as
2874 // separate params for extractSmartClipData(), and apply them not the new offset
2875 // values in the callback.
2876 final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2877 final int offsetXInDp = (int) (mSmartClipOffsetX / deviceScale);
2878 final int offsetYInDp = (int) (mSmartClipOffsetY / deviceScale);
2879 clipRect.offset(-offsetXInDp, -offsetYInDp);
2881 if (mSmartClipDataListener != null) {
2882 mSmartClipDataListener.onSmartClipDataExtracted(text, html, clipRect);
2886 public void setSmartClipDataListener(SmartClipDataListener listener) {
2887 mSmartClipDataListener = listener;
2890 public void setBackgroundOpaque(boolean opaque) {
2891 if (mNativeContentViewCore != 0) {
2892 nativeSetBackgroundOpaque(mNativeContentViewCore, opaque);
2897 * Offer a long press gesture to the embedding View, primarily for WebView compatibility.
2899 * @return true if the embedder handled the event.
2901 private boolean offerLongPressToEmbedder() {
2902 return mContainerView.performLongClick();
2906 * Reset scroll and fling accounting, notifying listeners as appropriate.
2907 * This is useful as a failsafe when the input stream may have been interruped.
2909 private void resetScrollInProgress() {
2910 if (!isScrollInProgress()) return;
2912 final boolean touchScrollInProgress = mTouchScrollInProgress;
2913 final int potentiallyActiveFlingCount = mPotentiallyActiveFlingCount;
2915 mTouchScrollInProgress = false;
2916 mPotentiallyActiveFlingCount = 0;
2918 if (touchScrollInProgress) updateGestureStateListener(GestureEventType.SCROLL_END);
2919 if (potentiallyActiveFlingCount > 0) updateGestureStateListener(GestureEventType.FLING_END);
2922 private native long nativeInit(long webContentsPtr,
2923 long viewAndroidPtr, long windowAndroidPtr, HashSet<Object> retainedObjectSet);
2925 ContentVideoViewClient getContentVideoViewClient() {
2926 return getContentViewClient().getContentVideoViewClient();
2930 private boolean shouldBlockMediaRequest(String url) {
2931 return getContentViewClient().shouldBlockMediaRequest(url);
2935 private void onNativeFlingStopped() {
2936 // Note that mTouchScrollInProgress should normally be false at this
2937 // point, but we reset it anyway as another failsafe.
2938 mTouchScrollInProgress = false;
2939 if (mPotentiallyActiveFlingCount <= 0) return;
2940 mPotentiallyActiveFlingCount--;
2941 updateGestureStateListener(GestureEventType.FLING_END);
2945 public void onScreenOrientationChanged(int orientation) {
2946 sendOrientationChangeEvent(orientation);
2950 * Set whether the ContentViewCore requires the WebContents to be fullscreen in order to lock
2951 * the screen orientation.
2953 public void setFullscreenRequiredForOrientationLock(boolean value) {
2954 mFullscreenRequiredForOrientationLock = value;
2958 private boolean isFullscreenRequiredForOrientationLock() {
2959 return mFullscreenRequiredForOrientationLock;
2962 private native WebContents nativeGetWebContentsAndroid(long nativeContentViewCoreImpl);
2964 private native void nativeOnJavaContentViewCoreDestroyed(long nativeContentViewCoreImpl);
2966 private native void nativeSetFocus(long nativeContentViewCoreImpl, boolean focused);
2968 private native void nativeSendOrientationChangeEvent(
2969 long nativeContentViewCoreImpl, int orientation);
2971 // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
2972 private native boolean nativeOnTouchEvent(
2973 long nativeContentViewCoreImpl, MotionEvent event,
2974 long timeMs, int action, int pointerCount, int historySize, int actionIndex,
2975 float x0, float y0, float x1, float y1,
2976 int pointerId0, int pointerId1,
2977 float touchMajor0, float touchMajor1,
2978 float touchMinor0, float touchMinor1,
2979 float orientation0, float orientation1,
2980 float rawX, float rawY,
2981 int androidToolType0, int androidToolType1,
2982 int androidButtonState, int androidMetaState,
2983 boolean isTouchHandleEvent);
2985 private native int nativeSendMouseMoveEvent(
2986 long nativeContentViewCoreImpl, long timeMs, float x, float y);
2988 private native int nativeSendMouseWheelEvent(
2989 long nativeContentViewCoreImpl, long timeMs, float x, float y, float verticalAxis);
2991 private native void nativeScrollBegin(
2992 long nativeContentViewCoreImpl, long timeMs, float x, float y, float hintX,
2995 private native void nativeScrollEnd(long nativeContentViewCoreImpl, long timeMs);
2997 private native void nativeScrollBy(
2998 long nativeContentViewCoreImpl, long timeMs, float x, float y,
2999 float deltaX, float deltaY);
3001 private native void nativeFlingStart(
3002 long nativeContentViewCoreImpl, long timeMs, float x, float y, float vx, float vy);
3004 private native void nativeFlingCancel(long nativeContentViewCoreImpl, long timeMs);
3006 private native void nativeSingleTap(
3007 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3009 private native void nativeDoubleTap(
3010 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3012 private native void nativeLongPress(
3013 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3015 private native void nativePinchBegin(
3016 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3018 private native void nativePinchEnd(long nativeContentViewCoreImpl, long timeMs);
3020 private native void nativePinchBy(long nativeContentViewCoreImpl, long timeMs,
3021 float anchorX, float anchorY, float deltaScale);
3023 private native void nativeSelectBetweenCoordinates(
3024 long nativeContentViewCoreImpl, float x1, float y1, float x2, float y2);
3026 private native void nativeMoveCaret(long nativeContentViewCoreImpl, float x, float y);
3028 private native void nativeDismissTextHandles(long nativeContentViewCoreImpl);
3029 private native void nativeSetTextHandlesTemporarilyHidden(
3030 long nativeContentViewCoreImpl, boolean hidden);
3032 private native void nativeResetGestureDetection(long nativeContentViewCoreImpl);
3034 private native void nativeSetDoubleTapSupportEnabled(
3035 long nativeContentViewCoreImpl, boolean enabled);
3037 private native void nativeSetMultiTouchZoomSupportEnabled(
3038 long nativeContentViewCoreImpl, boolean enabled);
3040 private native void nativeSelectPopupMenuItems(long nativeContentViewCoreImpl,
3041 long nativeSelectPopupSourceFrame, int[] indices);
3044 private native long nativeGetNativeImeAdapter(long nativeContentViewCoreImpl);
3046 private native int nativeGetCurrentRenderProcessId(long nativeContentViewCoreImpl);
3048 private native void nativeSetAllowJavascriptInterfacesInspection(
3049 long nativeContentViewCoreImpl, boolean allow);
3051 private native void nativeAddJavascriptInterface(long nativeContentViewCoreImpl, Object object,
3052 String name, Class requiredAnnotation);
3054 private native void nativeRemoveJavascriptInterface(long nativeContentViewCoreImpl,
3057 private native void nativeWasResized(long nativeContentViewCoreImpl);
3059 private native void nativeSetAccessibilityEnabled(
3060 long nativeContentViewCoreImpl, boolean enabled);
3062 private native void nativeExtractSmartClipData(long nativeContentViewCoreImpl,
3063 int x, int y, int w, int h);
3065 private native void nativeSetBackgroundOpaque(long nativeContentViewCoreImpl, boolean opaque);