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.ContentResolver;
11 import android.content.Context;
12 import android.content.Intent;
13 import android.content.pm.FeatureInfo;
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.Color;
20 import android.graphics.Rect;
21 import android.net.Uri;
22 import android.os.Build;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.os.ResultReceiver;
26 import android.os.SystemClock;
27 import android.provider.Browser;
28 import android.provider.Settings;
29 import android.text.Editable;
30 import android.text.Selection;
31 import android.text.TextUtils;
32 import android.util.Log;
33 import android.view.ActionMode;
34 import android.view.HapticFeedbackConstants;
35 import android.view.InputDevice;
36 import android.view.KeyEvent;
37 import android.view.MotionEvent;
38 import android.view.View;
39 import android.view.ViewGroup;
40 import android.view.accessibility.AccessibilityEvent;
41 import android.view.accessibility.AccessibilityManager;
42 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
43 import android.view.accessibility.AccessibilityNodeInfo;
44 import android.view.accessibility.AccessibilityNodeProvider;
45 import android.view.inputmethod.EditorInfo;
46 import android.view.inputmethod.InputConnection;
47 import android.view.inputmethod.InputMethodManager;
48 import android.widget.FrameLayout;
50 import com.google.common.annotations.VisibleForTesting;
52 import org.chromium.base.ApiCompatibilityUtils;
53 import org.chromium.base.CalledByNative;
54 import org.chromium.base.CommandLine;
55 import org.chromium.base.JNINamespace;
56 import org.chromium.base.ObserverList;
57 import org.chromium.base.ObserverList.RewindableIterator;
58 import org.chromium.base.TraceEvent;
59 import org.chromium.content.R;
60 import org.chromium.content.browser.ScreenOrientationListener.ScreenOrientationObserver;
61 import org.chromium.content.browser.accessibility.AccessibilityInjector;
62 import org.chromium.content.browser.accessibility.BrowserAccessibilityManager;
63 import org.chromium.content.browser.input.AdapterInputConnection;
64 import org.chromium.content.browser.input.GamepadList;
65 import org.chromium.content.browser.input.HandleView;
66 import org.chromium.content.browser.input.ImeAdapter;
67 import org.chromium.content.browser.input.ImeAdapter.AdapterInputConnectionFactory;
68 import org.chromium.content.browser.input.InputMethodManagerWrapper;
69 import org.chromium.content.browser.input.InsertionHandleController;
70 import org.chromium.content.browser.input.SelectPopup;
71 import org.chromium.content.browser.input.SelectPopupDialog;
72 import org.chromium.content.browser.input.SelectPopupDropdown;
73 import org.chromium.content.browser.input.SelectPopupItem;
74 import org.chromium.content.browser.input.SelectionHandleController;
75 import org.chromium.content.common.ContentSwitches;
76 import org.chromium.content_public.browser.GestureStateListener;
77 import org.chromium.content_public.browser.WebContents;
78 import org.chromium.ui.base.ViewAndroid;
79 import org.chromium.ui.base.ViewAndroidDelegate;
80 import org.chromium.ui.base.WindowAndroid;
81 import org.chromium.ui.gfx.DeviceDisplayInfo;
83 import java.lang.annotation.Annotation;
84 import java.lang.reflect.Field;
85 import java.util.ArrayList;
86 import java.util.HashMap;
87 import java.util.HashSet;
88 import java.util.List;
92 * Provides a Java-side 'wrapper' around a WebContent (native) instance.
93 * Contains all the major functionality necessary to manage the lifecycle of a ContentView without
94 * being tied to the view system.
96 @JNINamespace("content")
97 public class ContentViewCore
98 implements NavigationClient, AccessibilityStateChangeListener, ScreenOrientationObserver {
100 private static final String TAG = "ContentViewCore";
102 // Used to avoid enabling zooming in / out if resulting zooming will
103 // produce little visible difference.
104 private static final float ZOOM_CONTROLS_EPSILON = 0.007f;
106 // Used to represent gestures for long press and long tap.
107 private static final int IS_LONG_PRESS = 1;
108 private static final int IS_LONG_TAP = 2;
110 // Length of the delay (in ms) before fading in handles after the last page movement.
111 private static final int TEXT_HANDLE_FADE_IN_DELAY = 300;
113 // These values are obtained from Samsung.
114 // TODO(changwan): refactor SPen related code into a separate class. See
115 // http://crbug.com/398169.
116 private static final int SPEN_ACTION_DOWN = 211;
117 private static final int SPEN_ACTION_UP = 212;
118 private static final int SPEN_ACTION_MOVE = 213;
119 private static final int SPEN_ACTION_CANCEL = 214;
120 private static Boolean sIsSPenSupported;
122 // If the embedder adds a JavaScript interface object that contains an indirect reference to
123 // the ContentViewCore, then storing a strong ref to the interface object on the native
124 // side would prevent garbage collection of the ContentViewCore (as that strong ref would
125 // create a new GC root).
126 // For that reason, we store only a weak reference to the interface object on the
127 // native side. However we still need a strong reference on the Java side to
128 // prevent garbage collection if the embedder doesn't maintain their own ref to the
129 // interface object - the Java side ref won't create a new GC root.
130 // This map stores those refernces. We put into the map on addJavaScriptInterface()
131 // and remove from it in removeJavaScriptInterface().
132 private final Map<String, Object> mJavaScriptInterfaces = new HashMap<String, Object>();
134 // Additionally, we keep track of all Java bound JS objects that are in use on the
135 // current page to ensure that they are not garbage collected until the page is
136 // navigated. This includes interface objects that have been removed
137 // via the removeJavaScriptInterface API and transient objects returned from methods
138 // on the interface object. Note we use HashSet rather than Set as the native side
139 // expects HashSet (no bindings for interfaces).
140 private final HashSet<Object> mRetainedJavaScriptObjects = new HashSet<Object>();
143 * Interface that consumers of {@link ContentViewCore} must implement to allow the proper
144 * dispatching of view methods through the containing view.
147 * All methods with the "super_" prefix should be routed to the parent of the
148 * implementing container view.
150 @SuppressWarnings("javadoc")
151 public interface InternalAccessDelegate {
153 * @see View#drawChild(Canvas, View, long)
155 boolean drawChild(Canvas canvas, View child, long drawingTime);
158 * @see View#onKeyUp(keyCode, KeyEvent)
160 boolean super_onKeyUp(int keyCode, KeyEvent event);
163 * @see View#dispatchKeyEventPreIme(KeyEvent)
165 boolean super_dispatchKeyEventPreIme(KeyEvent event);
168 * @see View#dispatchKeyEvent(KeyEvent)
170 boolean super_dispatchKeyEvent(KeyEvent event);
173 * @see View#onGenericMotionEvent(MotionEvent)
175 boolean super_onGenericMotionEvent(MotionEvent event);
178 * @see View#onConfigurationChanged(Configuration)
180 void super_onConfigurationChanged(Configuration newConfig);
183 * @see View#onScrollChanged(int, int, int, int)
185 void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix);
188 * @see View#awakenScrollBars()
190 boolean awakenScrollBars();
193 * @see View#awakenScrollBars(int, boolean)
195 boolean super_awakenScrollBars(int startDelay, boolean invalidate);
199 * An interface for controlling visibility and state of embedder-provided zoom controls.
201 public interface ZoomControlsDelegate {
203 * Called when it's reasonable to show zoom controls.
205 void invokeZoomPicker();
208 * Called when zoom controls need to be hidden (e.g. when the view hides).
210 void dismissZoomPicker();
213 * Called when page scale has been changed, so the controls can update their state.
215 void updateZoomControls();
219 * An interface that allows the embedder to be notified when the results of
220 * extractSmartClipData are available.
222 public interface SmartClipDataListener {
223 public void onSmartClipDataExtracted(String text, String html, Rect clipRect);
226 private final Context mContext;
227 private ViewGroup mContainerView;
228 private InternalAccessDelegate mContainerViewInternals;
229 private WebContents mWebContents;
230 private WebContentsObserverAndroid mWebContentsObserver;
232 private ContentViewClient mContentViewClient;
234 private ContentSettings mContentSettings;
236 // Native pointer to C++ ContentViewCoreImpl object which will be set by nativeInit().
237 private long mNativeContentViewCore = 0;
239 private final ObserverList<GestureStateListener> mGestureStateListeners;
240 private final RewindableIterator<GestureStateListener> mGestureStateListenersIterator;
241 private ZoomControlsDelegate mZoomControlsDelegate;
243 private PopupZoomer mPopupZoomer;
244 private SelectPopup mSelectPopup;
246 private Runnable mFakeMouseMoveRunnable = null;
248 // Only valid when focused on a text / password field.
249 private ImeAdapter mImeAdapter;
250 private ImeAdapter.AdapterInputConnectionFactory mAdapterInputConnectionFactory;
251 private AdapterInputConnection mInputConnection;
252 private InputMethodManagerWrapper mInputMethodManagerWrapper;
254 private SelectionHandleController mSelectionHandleController;
255 private InsertionHandleController mInsertionHandleController;
257 private Runnable mDeferredHandleFadeInRunnable;
259 private PositionObserver mPositionObserver;
260 private PositionObserver.Listener mPositionListener;
262 // Size of the viewport in physical pixels as set from onSizeChanged.
263 private int mViewportWidthPix;
264 private int mViewportHeightPix;
265 private int mPhysicalBackingWidthPix;
266 private int mPhysicalBackingHeightPix;
267 private int mOverdrawBottomHeightPix;
268 private int mViewportSizeOffsetWidthPix;
269 private int mViewportSizeOffsetHeightPix;
271 // Cached copy of all positions and scales as reported by the renderer.
272 private final RenderCoordinates mRenderCoordinates;
274 private final RenderCoordinates.NormalizedPoint mStartHandlePoint;
275 private final RenderCoordinates.NormalizedPoint mEndHandlePoint;
276 private final RenderCoordinates.NormalizedPoint mInsertionHandlePoint;
278 // Tracks whether a selection is currently active. When applied to selected text, indicates
279 // whether the last selected text is still highlighted.
280 private boolean mHasSelection;
281 private String mLastSelectedText;
282 private boolean mSelectionEditable;
283 private ActionMode mActionMode;
284 private boolean mUnselectAllOnActionModeDismiss;
286 // Delegate that will handle GET downloads, and be notified of completion of POST downloads.
287 private ContentViewDownloadDelegate mDownloadDelegate;
289 // The AccessibilityInjector that handles loading Accessibility scripts into the web page.
290 private AccessibilityInjector mAccessibilityInjector;
292 // Whether native accessibility, i.e. without any script injection, is allowed.
293 private boolean mNativeAccessibilityAllowed;
295 // Whether native accessibility, i.e. without any script injection, has been enabled.
296 private boolean mNativeAccessibilityEnabled;
298 // Handles native accessibility, i.e. without any script injection.
299 private BrowserAccessibilityManager mBrowserAccessibilityManager;
301 // System accessibility service.
302 private final AccessibilityManager mAccessibilityManager;
304 // Accessibility touch exploration state.
305 private boolean mTouchExplorationEnabled;
307 // Allows us to dynamically respond when the accessibility script injection flag changes.
308 private ContentObserver mAccessibilityScriptInjectionObserver;
310 // Temporary notification to tell onSizeChanged to focus a form element,
311 // because the OSK was just brought up.
312 private final Rect mFocusPreOSKViewportRect = new Rect();
314 // On tap this will store the x, y coordinates of the touch.
315 private int mLastTapX;
316 private int mLastTapY;
318 // Whether a touch scroll sequence is active, used to hide text selection
319 // handles. Note that a scroll sequence will *always* bound a pinch
320 // sequence, so this will also be true for the duration of a pinch gesture.
321 private boolean mTouchScrollInProgress;
323 // The outstanding fling start events that hasn't got fling end yet. It may be > 1 because
324 // onNativeFlingStopped() is called asynchronously.
325 private int mPotentiallyActiveFlingCount;
327 private ViewAndroid mViewAndroid;
329 private SmartClipDataListener mSmartClipDataListener = null;
331 // This holds the state of editable text (e.g. contents of <input>, contenteditable) of
332 // a focused element.
333 // Every time the user, IME, javascript (Blink), autofill etc. modifies the content, the new
334 // state must be reflected to this to keep consistency.
335 private final Editable mEditable;
338 * PID used to indicate an invalid render process.
340 // Keep in sync with the value returned from ContentViewCoreImpl::GetCurrentRendererProcessId()
341 // if there is no render process.
342 public static final int INVALID_RENDER_PROCESS_PID = 0;
344 // Offsets for the events that passes through this ContentViewCore.
345 private float mCurrentTouchOffsetX;
346 private float mCurrentTouchOffsetY;
348 // Offsets for smart clip
349 private int mSmartClipOffsetX;
350 private int mSmartClipOffsetY;
353 * Constructs a new ContentViewCore. Embedders must call initialize() after constructing
354 * a ContentViewCore and before using it.
356 * @param context The context used to create this.
358 public ContentViewCore(Context context) {
361 mAdapterInputConnectionFactory = new AdapterInputConnectionFactory();
362 mInputMethodManagerWrapper = new InputMethodManagerWrapper(mContext);
364 mRenderCoordinates = new RenderCoordinates();
365 float deviceScaleFactor = getContext().getResources().getDisplayMetrics().density;
366 String forceScaleFactor = CommandLine.getInstance().getSwitchValue(
367 ContentSwitches.FORCE_DEVICE_SCALE_FACTOR);
368 if (forceScaleFactor != null) {
369 deviceScaleFactor = Float.valueOf(forceScaleFactor);
371 mRenderCoordinates.setDeviceScaleFactor(deviceScaleFactor);
372 mStartHandlePoint = mRenderCoordinates.createNormalizedPoint();
373 mEndHandlePoint = mRenderCoordinates.createNormalizedPoint();
374 mInsertionHandlePoint = mRenderCoordinates.createNormalizedPoint();
375 mAccessibilityManager = (AccessibilityManager)
376 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
377 mGestureStateListeners = new ObserverList<GestureStateListener>();
378 mGestureStateListenersIterator = mGestureStateListeners.rewindableIterator();
380 mEditable = Editable.Factory.getInstance().newEditable("");
381 Selection.setSelection(mEditable, 0);
385 * @return The context used for creating this ContentViewCore.
388 public Context getContext() {
393 * @return The ViewGroup that all view actions of this ContentViewCore should interact with.
395 public ViewGroup getContainerView() {
396 return mContainerView;
400 * @return The WebContents currently being rendered.
402 public WebContents getWebContents() {
407 * Specifies how much smaller the WebKit layout size should be relative to the size of this
409 * @param offsetXPix The X amount in pixels to shrink the viewport by.
410 * @param offsetYPix The Y amount in pixels to shrink the viewport by.
412 public void setViewportSizeOffset(int offsetXPix, int offsetYPix) {
413 if (offsetXPix != mViewportSizeOffsetWidthPix ||
414 offsetYPix != mViewportSizeOffsetHeightPix) {
415 mViewportSizeOffsetWidthPix = offsetXPix;
416 mViewportSizeOffsetHeightPix = offsetYPix;
417 if (mNativeContentViewCore != 0) nativeWasResized(mNativeContentViewCore);
422 * Returns a delegate that can be used to add and remove views from the ContainerView.
424 * NOTE: Use with care, as not all ContentViewCore users setup their ContainerView in the same
425 * way. In particular, the Android WebView has limitations on what implementation details can
426 * be provided via a child view, as they are visible in the API and could introduce
427 * compatibility breaks with existing applications. If in doubt, contact the
428 * android_webview/OWNERS
430 * @return A ViewAndroidDelegate that can be used to add and remove views.
433 public ViewAndroidDelegate getViewAndroidDelegate() {
434 return new ViewAndroidDelegate() {
435 // mContainerView can change, but this ViewAndroidDelegate can only be used to
436 // add and remove views from the mContainerViewAtCreation.
437 private final ViewGroup mContainerViewAtCreation = mContainerView;
440 public View acquireAnchorView() {
441 View anchorView = new View(mContext);
442 mContainerViewAtCreation.addView(anchorView);
447 @SuppressWarnings("deprecation") // AbsoluteLayout
448 public void setAnchorViewPosition(
449 View view, float x, float y, float width, float height) {
450 assert view.getParent() == mContainerViewAtCreation;
452 float scale = (float) DeviceDisplayInfo.create(mContext).getDIPScale();
454 // The anchor view should not go outside the bounds of the ContainerView.
455 int leftMargin = Math.round(x * scale);
456 int topMargin = Math.round(mRenderCoordinates.getContentOffsetYPix() + y * scale);
457 int scaledWidth = Math.round(width * scale);
458 // ContentViewCore currently only supports these two container view types.
459 if (mContainerViewAtCreation instanceof FrameLayout) {
461 if (ApiCompatibilityUtils.isLayoutRtl(mContainerViewAtCreation)) {
462 startMargin = mContainerViewAtCreation.getMeasuredWidth()
463 - Math.round((width + x) * scale);
465 startMargin = leftMargin;
467 if (scaledWidth + startMargin > mContainerViewAtCreation.getWidth()) {
468 scaledWidth = mContainerViewAtCreation.getWidth() - startMargin;
470 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
471 scaledWidth, Math.round(height * scale));
472 ApiCompatibilityUtils.setMarginStart(lp, startMargin);
473 lp.topMargin = topMargin;
474 view.setLayoutParams(lp);
475 } else if (mContainerViewAtCreation instanceof android.widget.AbsoluteLayout) {
476 // This fixes the offset due to a difference in
477 // scrolling model of WebView vs. Chrome.
478 // TODO(sgurun) fix this to use mContainerViewAtCreation.getScroll[X/Y]()
479 // as it naturally accounts for scroll differences between
481 leftMargin += mRenderCoordinates.getScrollXPixInt();
482 topMargin += mRenderCoordinates.getScrollYPixInt();
484 android.widget.AbsoluteLayout.LayoutParams lp =
485 new android.widget.AbsoluteLayout.LayoutParams(
486 scaledWidth, (int) (height * scale), leftMargin, topMargin);
487 view.setLayoutParams(lp);
489 Log.e(TAG, "Unknown layout " + mContainerViewAtCreation.getClass().getName());
494 public void releaseAnchorView(View anchorView) {
495 mContainerViewAtCreation.removeView(anchorView);
501 public void setImeAdapterForTest(ImeAdapter imeAdapter) {
502 mImeAdapter = imeAdapter;
506 public ImeAdapter getImeAdapterForTest() {
511 public void setAdapterInputConnectionFactory(AdapterInputConnectionFactory factory) {
512 mAdapterInputConnectionFactory = factory;
516 public void setInputMethodManagerWrapperForTest(InputMethodManagerWrapper immw) {
517 mInputMethodManagerWrapper = immw;
521 public AdapterInputConnection getInputConnectionForTest() {
522 return mInputConnection;
525 private ImeAdapter createImeAdapter(Context context) {
526 return new ImeAdapter(mInputMethodManagerWrapper,
527 new ImeAdapter.ImeAdapterDelegate() {
529 public void onImeEvent(boolean isFinish) {
530 getContentViewClient().onImeEvent();
537 public void onDismissInput() {
538 getContentViewClient().onImeStateChangeRequested(false);
542 public View getAttachedView() {
543 return mContainerView;
547 public ResultReceiver getNewShowKeyboardReceiver() {
548 return new ResultReceiver(new Handler()) {
550 public void onReceiveResult(int resultCode, Bundle resultData) {
551 getContentViewClient().onImeStateChangeRequested(
552 resultCode == InputMethodManager.RESULT_SHOWN ||
553 resultCode == InputMethodManager.RESULT_UNCHANGED_SHOWN);
554 if (resultCode == InputMethodManager.RESULT_SHOWN) {
555 // If OSK is newly shown, delay the form focus until
556 // the onSizeChanged (in order to adjust relative to the
558 // TODO(jdduke): We should not assume that onSizeChanged will
559 // always be called, crbug.com/294908.
560 getContainerView().getWindowVisibleDisplayFrame(
561 mFocusPreOSKViewportRect);
562 } else if (hasFocus() && resultCode ==
563 InputMethodManager.RESULT_UNCHANGED_SHOWN) {
564 // If the OSK was already there, focus the form immediately.
565 scrollFocusedEditableNodeIntoView();
576 * @param containerView The view that will act as a container for all views created by this.
577 * @param internalDispatcher Handles dispatching all hidden or super methods to the
579 * @param nativeWebContents A pointer to the native web contents.
580 * @param windowAndroid An instance of the WindowAndroid.
582 // Perform important post-construction set up of the ContentViewCore.
583 // We do not require the containing view in the constructor to allow embedders to create a
584 // ContentViewCore without having fully created its containing view. The containing view
585 // is a vital component of the ContentViewCore, so embedders must exercise caution in what
586 // they do with the ContentViewCore before calling initialize().
587 // We supply the nativeWebContents pointer here rather than in the constructor to allow us
588 // to set the private browsing mode at a later point for the WebView implementation.
589 // Note that the caller remains the owner of the nativeWebContents and is responsible for
590 // deleting it after destroying the ContentViewCore.
591 public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
592 long nativeWebContents, WindowAndroid windowAndroid) {
593 setContainerView(containerView);
595 mPositionListener = new PositionObserver.Listener() {
597 public void onPositionChanged(int x, int y) {
598 if (isSelectionHandleShowing() || isInsertionHandleShowing()) {
599 temporarilyHideTextHandles();
604 long windowNativePointer = windowAndroid != null ? windowAndroid.getNativePointer() : 0;
606 long viewAndroidNativePointer = 0;
607 if (windowNativePointer != 0) {
608 mViewAndroid = new ViewAndroid(windowAndroid, getViewAndroidDelegate());
609 viewAndroidNativePointer = mViewAndroid.getNativePointer();
612 mZoomControlsDelegate = new ZoomControlsDelegate() {
614 public void invokeZoomPicker() {}
616 public void dismissZoomPicker() {}
618 public void updateZoomControls() {}
621 mNativeContentViewCore = nativeInit(
622 nativeWebContents, viewAndroidNativePointer, windowNativePointer,
623 mRetainedJavaScriptObjects);
624 mWebContents = nativeGetWebContentsAndroid(mNativeContentViewCore);
625 mContentSettings = new ContentSettings(this, mNativeContentViewCore);
627 setContainerViewInternals(internalDispatcher);
628 mRenderCoordinates.reset();
629 initPopupZoomer(mContext);
630 mImeAdapter = createImeAdapter(mContext);
632 mAccessibilityInjector = AccessibilityInjector.newInstance(this);
634 mWebContentsObserver = new WebContentsObserverAndroid(this) {
636 public void didNavigateMainFrame(String url, String baseUrl,
637 boolean isNavigationToDifferentPage, boolean isFragmentNavigation) {
638 if (!isNavigationToDifferentPage) return;
640 resetScrollInProgress();
641 resetGestureDetection();
645 public void renderProcessGone(boolean wasOomProtected) {
647 resetScrollInProgress();
648 // No need to reset gesture detection as the detector will have
649 // been destroyed in the RenderWidgetHostView.
655 * Sets a new container view for this {@link ContentViewCore}.
657 * <p>WARNING: This is not a general purpose method and has been designed with WebView
658 * fullscreen in mind. Please be aware that it might not be appropriate for other use cases
659 * and that it has a number of limitations. For example the PopupZoomer only works with the
660 * container view with which this ContentViewCore has been initialized.
662 * <p>This method only performs a small part of replacing the container view and
663 * embedders are responsible for:
665 * <li>Disconnecting the old container view from this ContentViewCore</li>
666 * <li>Updating the InternalAccessDelegate</li>
667 * <li>Reconciling the state of this ContentViewCore with the new container view</li>
668 * <li>Tearing down and recreating the native GL rendering where appropriate</li>
672 public void setContainerView(ViewGroup containerView) {
674 if (mContainerView != null) {
675 mPositionObserver.removeListener(mPositionListener);
676 mSelectionHandleController = null;
677 mInsertionHandleController = null;
678 mInputConnection = null;
681 mContainerView = containerView;
682 mPositionObserver = new ViewPositionObserver(mContainerView);
683 String contentDescription = "Web View";
684 if (R.string.accessibility_content_view == 0) {
685 Log.w(TAG, "Setting contentDescription to 'Web View' as no value was specified.");
687 contentDescription = mContext.getResources().getString(
688 R.string.accessibility_content_view);
690 mContainerView.setContentDescription(contentDescription);
691 mContainerView.setWillNotDraw(false);
692 mContainerView.setClickable(true);
697 void onNativeContentViewCoreDestroyed(long nativeContentViewCore) {
698 assert nativeContentViewCore == mNativeContentViewCore;
699 mNativeContentViewCore = 0;
703 * Set the Container view Internals.
704 * @param internalDispatcher Handles dispatching all hidden or super methods to the
707 public void setContainerViewInternals(InternalAccessDelegate internalDispatcher) {
708 mContainerViewInternals = internalDispatcher;
711 private void initPopupZoomer(Context context) {
712 mPopupZoomer = new PopupZoomer(context);
713 mPopupZoomer.setOnVisibilityChangedListener(new PopupZoomer.OnVisibilityChangedListener() {
714 // mContainerView can change, but this OnVisibilityChangedListener can only be used
715 // to add and remove views from the mContainerViewAtCreation.
716 private final ViewGroup mContainerViewAtCreation = mContainerView;
719 public void onPopupZoomerShown(final PopupZoomer zoomer) {
720 mContainerViewAtCreation.post(new Runnable() {
723 if (mContainerViewAtCreation.indexOfChild(zoomer) == -1) {
724 mContainerViewAtCreation.addView(zoomer);
726 assert false : "PopupZoomer should never be shown without being hidden";
733 public void onPopupZoomerHidden(final PopupZoomer zoomer) {
734 mContainerViewAtCreation.post(new Runnable() {
737 if (mContainerViewAtCreation.indexOfChild(zoomer) != -1) {
738 mContainerViewAtCreation.removeView(zoomer);
739 mContainerViewAtCreation.invalidate();
741 assert false : "PopupZoomer should never be hidden without being shown";
747 // TODO(yongsheng): LONG_TAP is not enabled in PopupZoomer. So need to dispatch a LONG_TAP
748 // gesture if a user completes a tap on PopupZoomer UI after a LONG_PRESS gesture.
749 PopupZoomer.OnTapListener listener = new PopupZoomer.OnTapListener() {
750 // mContainerView can change, but this OnTapListener can only be used
751 // with the mContainerViewAtCreation.
752 private final ViewGroup mContainerViewAtCreation = mContainerView;
755 public boolean onSingleTap(View v, MotionEvent e) {
756 mContainerViewAtCreation.requestFocus();
757 if (mNativeContentViewCore != 0) {
758 nativeSingleTap(mNativeContentViewCore, e.getEventTime(), e.getX(), e.getY());
764 public boolean onLongPress(View v, MotionEvent e) {
765 if (mNativeContentViewCore != 0) {
766 nativeLongPress(mNativeContentViewCore, e.getEventTime(), e.getX(), e.getY());
771 mPopupZoomer.setOnTapListener(listener);
775 * Destroy the internal state of the ContentView. This method may only be
776 * called after the ContentView has been removed from the view system. No
777 * other methods may be called on this ContentView after this method has
780 public void destroy() {
781 if (mNativeContentViewCore != 0) {
782 nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore);
785 if (mViewAndroid != null) mViewAndroid.destroy();
786 mNativeContentViewCore = 0;
787 mContentSettings = null;
788 mJavaScriptInterfaces.clear();
789 mRetainedJavaScriptObjects.clear();
790 unregisterAccessibilityContentObserver();
791 mGestureStateListeners.clear();
792 ScreenOrientationListener.getInstance().removeObserver(this);
795 private void unregisterAccessibilityContentObserver() {
796 if (mAccessibilityScriptInjectionObserver == null) {
799 getContext().getContentResolver().unregisterContentObserver(
800 mAccessibilityScriptInjectionObserver);
801 mAccessibilityScriptInjectionObserver = null;
805 * Returns true initially, false after destroy() has been called.
806 * It is illegal to call any other public method after destroy().
808 public boolean isAlive() {
809 return mNativeContentViewCore != 0;
813 * This is only useful for passing over JNI to native code that requires ContentViewCore*.
814 * @return native ContentViewCore pointer.
817 public long getNativeContentViewCore() {
818 return mNativeContentViewCore;
821 public void setContentViewClient(ContentViewClient client) {
822 if (client == null) {
823 throw new IllegalArgumentException("The client can't be null.");
825 mContentViewClient = client;
829 public ContentViewClient getContentViewClient() {
830 if (mContentViewClient == null) {
831 // We use the Null Object pattern to avoid having to perform a null check in this class.
832 // We create it lazily because most of the time a client will be set almost immediately
833 // after ContentView is created.
834 mContentViewClient = new ContentViewClient();
835 // We don't set the native ContentViewClient pointer here on purpose. The native
836 // implementation doesn't mind a null delegate and using one is better than passing a
837 // Null Object, since we cut down on the number of JNI calls.
839 return mContentViewClient;
842 public int getBackgroundColor() {
843 if (mNativeContentViewCore != 0) {
844 return nativeGetBackgroundColor(mNativeContentViewCore);
850 private void onBackgroundColorChanged(int color) {
851 getContentViewClient().onBackgroundColorChanged(color);
855 * Load url without fixing up the url string. Consumers of ContentView are responsible for
856 * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
857 * off during user input).
859 * @param params Parameters for this load.
861 public void loadUrl(LoadUrlParams params) {
862 if (mNativeContentViewCore == 0) return;
864 nativeLoadUrl(mNativeContentViewCore,
867 params.mTransitionType,
868 params.getReferrer() != null ? params.getReferrer().getUrl() : null,
869 params.getReferrer() != null ? params.getReferrer().getPolicy() : 0,
870 params.mUaOverrideOption,
871 params.getExtraHeadersString(),
873 params.mBaseUrlForDataUrl,
874 params.mVirtualUrlForDataUrl,
875 params.mCanLoadLocalResources,
876 params.mIsRendererInitiated);
880 * Stops loading the current web contents.
882 public void stopLoading() {
883 if (mWebContents != null) mWebContents.stop();
887 * Get the URL of the current page.
889 * @return The URL of the current page.
891 public String getUrl() {
892 if (mNativeContentViewCore != 0) return nativeGetURL(mNativeContentViewCore);
897 * Get the title of the current page.
899 * @return The title of the current page.
901 public String getTitle() {
902 return mWebContents == null ? null : mWebContents.getTitle();
906 * Shows an interstitial page driven by the passed in delegate.
908 * @param url The URL being blocked by the interstitial.
909 * @param delegate The delegate handling the interstitial.
912 public void showInterstitialPage(
913 String url, InterstitialPageDelegateAndroid delegate) {
914 if (mNativeContentViewCore == 0) return;
915 nativeShowInterstitialPage(mNativeContentViewCore, url, delegate.getNative());
919 * @return Whether the page is currently showing an interstitial, such as a bad HTTPS page.
921 public boolean isShowingInterstitialPage() {
922 return mNativeContentViewCore == 0 ?
923 false : nativeIsShowingInterstitialPage(mNativeContentViewCore);
927 * @return Viewport width in physical pixels as set from onSizeChanged.
930 public int getViewportWidthPix() { return mViewportWidthPix; }
933 * @return Viewport height in physical pixels as set from onSizeChanged.
936 public int getViewportHeightPix() { return mViewportHeightPix; }
939 * @return Width of underlying physical surface.
942 public int getPhysicalBackingWidthPix() { return mPhysicalBackingWidthPix; }
945 * @return Height of underlying physical surface.
948 public int getPhysicalBackingHeightPix() { return mPhysicalBackingHeightPix; }
951 * @return Amount the output surface extends past the bottom of the window viewport.
954 public int getOverdrawBottomHeightPix() { return mOverdrawBottomHeightPix; }
957 * @return The amount to shrink the viewport relative to {@link #getViewportWidthPix()}.
960 public int getViewportSizeOffsetWidthPix() { return mViewportSizeOffsetWidthPix; }
963 * @return The amount to shrink the viewport relative to {@link #getViewportHeightPix()}.
966 public int getViewportSizeOffsetHeightPix() { return mViewportSizeOffsetHeightPix; }
969 * @see android.webkit.WebView#getContentHeight()
971 public float getContentHeightCss() {
972 return mRenderCoordinates.getContentHeightCss();
976 * @see android.webkit.WebView#getContentWidth()
978 public float getContentWidthCss() {
979 return mRenderCoordinates.getContentWidthCss();
982 // TODO(teddchoc): Remove all these navigation controller methods from here and have the
983 // embedders manage it.
985 * @return Whether the current WebContents has a previous navigation entry.
987 public boolean canGoBack() {
988 return mWebContents != null && mWebContents.getNavigationController().canGoBack();
992 * @return Whether the current WebContents has a navigation entry after the current one.
994 public boolean canGoForward() {
995 return mWebContents != null && mWebContents.getNavigationController().canGoForward();
999 * @param offset The offset into the navigation history.
1000 * @return Whether we can move in history by given offset
1002 public boolean canGoToOffset(int offset) {
1003 return mWebContents != null &&
1004 mWebContents.getNavigationController().canGoToOffset(offset);
1008 * Navigates to the specified offset from the "current entry". Does nothing if the offset is out
1010 * @param offset The offset into the navigation history.
1012 public void goToOffset(int offset) {
1013 if (mWebContents != null) mWebContents.getNavigationController().goToOffset(offset);
1017 public void goToNavigationIndex(int index) {
1018 if (mWebContents != null) {
1019 mWebContents.getNavigationController().goToNavigationIndex(index);
1024 * Goes to the navigation entry before the current one.
1026 public void goBack() {
1027 if (mWebContents != null) mWebContents.getNavigationController().goBack();
1031 * Goes to the navigation entry following the current one.
1033 public void goForward() {
1034 if (mWebContents != null) mWebContents.getNavigationController().goForward();
1038 * Loads the current navigation if there is a pending lazy load (after tab restore).
1040 public void loadIfNecessary() {
1041 if (mNativeContentViewCore != 0) nativeLoadIfNecessary(mNativeContentViewCore);
1045 * Requests the current navigation to be loaded upon the next call to loadIfNecessary().
1047 public void requestRestoreLoad() {
1048 if (mNativeContentViewCore != 0) nativeRequestRestoreLoad(mNativeContentViewCore);
1052 * Reload the current page.
1054 public void reload(boolean checkForRepost) {
1055 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1056 if (mNativeContentViewCore != 0) {
1057 nativeReload(mNativeContentViewCore, checkForRepost);
1062 * Reload the current page, ignoring the contents of the cache.
1064 public void reloadIgnoringCache(boolean checkForRepost) {
1065 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1066 if (mNativeContentViewCore != 0) {
1067 nativeReloadIgnoringCache(mNativeContentViewCore, checkForRepost);
1072 * Cancel the pending reload.
1074 public void cancelPendingReload() {
1075 if (mNativeContentViewCore != 0) nativeCancelPendingReload(mNativeContentViewCore);
1079 * Continue the pending reload.
1081 public void continuePendingReload() {
1082 if (mNativeContentViewCore != 0) nativeContinuePendingReload(mNativeContentViewCore);
1086 * Clears the ContentViewCore's page history in both the backwards and
1087 * forwards directions.
1089 public void clearHistory() {
1090 if (mNativeContentViewCore != 0) nativeClearHistory(mNativeContentViewCore);
1094 * @return The selected text (empty if no text selected).
1096 public String getSelectedText() {
1097 return mHasSelection ? mLastSelectedText : "";
1101 * @return Whether the current selection is editable (false if no text selected).
1103 public boolean isSelectionEditable() {
1104 return mHasSelection ? mSelectionEditable : false;
1107 // End FrameLayout overrides.
1110 * TODO(changwan): refactor SPen related code into a separate class. See
1111 * http://crbug.com/398169.
1112 * @return Whether SPen is supported on the device.
1114 public static boolean isSPenSupported(Context context) {
1115 if (sIsSPenSupported == null)
1116 sIsSPenSupported = detectSPenSupport(context);
1117 return sIsSPenSupported.booleanValue();
1120 private static boolean detectSPenSupport(Context context) {
1121 if (!"SAMSUNG".equalsIgnoreCase(Build.MANUFACTURER))
1124 final FeatureInfo[] infos = context.getPackageManager().getSystemAvailableFeatures();
1125 for (FeatureInfo info : infos) {
1126 if ("com.sec.feature.spen_usp".equalsIgnoreCase(info.name)) {
1134 * Convert SPen event action into normal event action.
1135 * TODO(changwan): refactor SPen related code into a separate class. See
1136 * http://crbug.com/398169.
1138 * @param eventActionMasked Input event action. It is assumed that it is masked as the values
1140 * @return Event action after the conversion
1142 public static int convertSPenEventAction(int eventActionMasked) {
1143 // S-Pen support: convert to normal stylus event handling
1144 switch (eventActionMasked) {
1145 case SPEN_ACTION_DOWN:
1146 return MotionEvent.ACTION_DOWN;
1147 case SPEN_ACTION_UP:
1148 return MotionEvent.ACTION_UP;
1149 case SPEN_ACTION_MOVE:
1150 return MotionEvent.ACTION_MOVE;
1151 case SPEN_ACTION_CANCEL:
1152 return MotionEvent.ACTION_CANCEL;
1154 return eventActionMasked;
1159 * @see View#onTouchEvent(MotionEvent)
1161 public boolean onTouchEvent(MotionEvent event) {
1162 TraceEvent.begin("onTouchEvent");
1164 cancelRequestToScrollFocusedEditableNodeIntoView();
1166 int eventAction = event.getActionMasked();
1168 if (isSPenSupported(mContext))
1169 eventAction = convertSPenEventAction(eventAction);
1171 // Only these actions have any effect on gesture detection. Other
1172 // actions have no corresponding WebTouchEvent type and may confuse the
1173 // touch pipline, so we ignore them entirely.
1174 if (eventAction != MotionEvent.ACTION_DOWN
1175 && eventAction != MotionEvent.ACTION_UP
1176 && eventAction != MotionEvent.ACTION_CANCEL
1177 && eventAction != MotionEvent.ACTION_MOVE
1178 && eventAction != MotionEvent.ACTION_POINTER_DOWN
1179 && eventAction != MotionEvent.ACTION_POINTER_UP) {
1183 if (mNativeContentViewCore == 0) return false;
1185 // A zero offset is quite common, in which case the unnecessary copy should be avoided.
1186 MotionEvent offset = null;
1187 if (mCurrentTouchOffsetX != 0 || mCurrentTouchOffsetY != 0) {
1188 offset = createOffsetMotionEvent(event);
1192 final int pointerCount = event.getPointerCount();
1193 final boolean consumed = nativeOnTouchEvent(mNativeContentViewCore, event,
1194 event.getEventTime(), eventAction,
1195 pointerCount, event.getHistorySize(), event.getActionIndex(),
1196 event.getX(), event.getY(),
1197 pointerCount > 1 ? event.getX(1) : 0,
1198 pointerCount > 1 ? event.getY(1) : 0,
1199 event.getPointerId(0), pointerCount > 1 ? event.getPointerId(1) : -1,
1200 event.getTouchMajor(), pointerCount > 1 ? event.getTouchMajor(1) : 0,
1201 event.getRawX(), event.getRawY(),
1202 event.getToolType(0),
1203 pointerCount > 1 ? event.getToolType(1) : MotionEvent.TOOL_TYPE_UNKNOWN,
1204 event.getButtonState());
1206 if (offset != null) offset.recycle();
1209 TraceEvent.end("onTouchEvent");
1213 public void setIgnoreRemainingTouchEvents() {
1214 resetGestureDetection();
1217 public boolean isScrollInProgress() {
1218 return mTouchScrollInProgress || mPotentiallyActiveFlingCount > 0;
1221 @SuppressWarnings("unused")
1223 private void onFlingStartEventConsumed(int vx, int vy) {
1224 mTouchScrollInProgress = false;
1225 mPotentiallyActiveFlingCount++;
1226 temporarilyHideTextHandles();
1227 for (mGestureStateListenersIterator.rewind();
1228 mGestureStateListenersIterator.hasNext();) {
1229 mGestureStateListenersIterator.next().onFlingStartGesture(
1230 vx, vy, computeVerticalScrollOffset(), computeVerticalScrollExtent());
1234 @SuppressWarnings("unused")
1236 private void onFlingStartEventHadNoConsumer(int vx, int vy) {
1237 mTouchScrollInProgress = false;
1238 for (mGestureStateListenersIterator.rewind();
1239 mGestureStateListenersIterator.hasNext();) {
1240 mGestureStateListenersIterator.next().onUnhandledFlingStartEvent(vx, vy);
1244 @SuppressWarnings("unused")
1246 private void onFlingCancelEventAck() {
1247 updateGestureStateListener(GestureEventType.FLING_CANCEL);
1250 @SuppressWarnings("unused")
1252 private void onScrollBeginEventAck() {
1253 mTouchScrollInProgress = true;
1254 temporarilyHideTextHandles();
1255 mZoomControlsDelegate.invokeZoomPicker();
1256 updateGestureStateListener(GestureEventType.SCROLL_START);
1259 @SuppressWarnings("unused")
1261 private void onScrollUpdateGestureConsumed() {
1262 mZoomControlsDelegate.invokeZoomPicker();
1263 for (mGestureStateListenersIterator.rewind();
1264 mGestureStateListenersIterator.hasNext();) {
1265 mGestureStateListenersIterator.next().onScrollUpdateGestureConsumed();
1269 @SuppressWarnings("unused")
1271 private void onScrollEndEventAck() {
1272 if (!mTouchScrollInProgress) return;
1273 mTouchScrollInProgress = false;
1274 updateGestureStateListener(GestureEventType.SCROLL_END);
1277 @SuppressWarnings("unused")
1279 private void onPinchBeginEventAck() {
1280 temporarilyHideTextHandles();
1281 updateGestureStateListener(GestureEventType.PINCH_BEGIN);
1284 @SuppressWarnings("unused")
1286 private void onPinchEndEventAck() {
1287 updateGestureStateListener(GestureEventType.PINCH_END);
1290 @SuppressWarnings("unused")
1292 private void onSingleTapEventAck(boolean consumed, int x, int y) {
1293 for (mGestureStateListenersIterator.rewind();
1294 mGestureStateListenersIterator.hasNext();) {
1295 mGestureStateListenersIterator.next().onSingleTap(consumed, x, y);
1299 @SuppressWarnings("unused")
1301 private void onDoubleTapEventAck() {
1302 temporarilyHideTextHandles();
1306 * Called just prior to a tap or press gesture being forwarded to the renderer.
1308 @SuppressWarnings("unused")
1310 private boolean filterTapOrPressEvent(int type, int x, int y) {
1311 if (type == GestureEventType.LONG_PRESS && offerLongPressToEmbedder()) {
1314 updateForTapOrPress(type, x, y);
1319 public void sendDoubleTapForTest(long timeMs, int x, int y) {
1320 if (mNativeContentViewCore == 0) return;
1321 nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1325 public void flingForTest(long timeMs, int x, int y, int velocityX, int velocityY) {
1326 if (mNativeContentViewCore == 0) return;
1327 nativeFlingCancel(mNativeContentViewCore, timeMs);
1328 nativeScrollBegin(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1329 nativeFlingStart(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1333 * Cancel any fling gestures active.
1334 * @param timeMs Current time (in milliseconds).
1336 public void cancelFling(long timeMs) {
1337 if (mNativeContentViewCore == 0) return;
1338 nativeFlingCancel(mNativeContentViewCore, timeMs);
1342 * Add a listener that gets alerted on gesture state changes.
1343 * @param listener Listener to add.
1345 public void addGestureStateListener(GestureStateListener listener) {
1346 mGestureStateListeners.addObserver(listener);
1350 * Removes a listener that was added to watch for gesture state changes.
1351 * @param listener Listener to remove.
1353 public void removeGestureStateListener(GestureStateListener listener) {
1354 mGestureStateListeners.removeObserver(listener);
1357 void updateGestureStateListener(int gestureType) {
1358 for (mGestureStateListenersIterator.rewind();
1359 mGestureStateListenersIterator.hasNext();) {
1360 GestureStateListener listener = mGestureStateListenersIterator.next();
1361 switch (gestureType) {
1362 case GestureEventType.PINCH_BEGIN:
1363 listener.onPinchStarted();
1365 case GestureEventType.PINCH_END:
1366 listener.onPinchEnded();
1368 case GestureEventType.FLING_END:
1369 listener.onFlingEndGesture(
1370 computeVerticalScrollOffset(),
1371 computeVerticalScrollExtent());
1373 case GestureEventType.FLING_CANCEL:
1374 listener.onFlingCancelGesture();
1376 case GestureEventType.SCROLL_START:
1377 listener.onScrollStarted(
1378 computeVerticalScrollOffset(),
1379 computeVerticalScrollExtent());
1381 case GestureEventType.SCROLL_END:
1382 listener.onScrollEnded(
1383 computeVerticalScrollOffset(),
1384 computeVerticalScrollExtent());
1393 * Requests the renderer insert a link to the specified stylesheet in the
1394 * main frame's document.
1396 void addStyleSheetByURL(String url) {
1397 nativeAddStyleSheetByURL(mNativeContentViewCore, url);
1400 /** Callback interface for evaluateJavaScript(). */
1401 public interface JavaScriptCallback {
1402 void handleJavaScriptResult(String jsonResult);
1406 * Injects the passed Javascript code in the current page and evaluates it.
1407 * If a result is required, pass in a callback.
1408 * Used in automation tests.
1410 * @param script The Javascript to execute.
1411 * @param callback The callback to be fired off when a result is ready. The script's
1412 * result will be json encoded and passed as the parameter, and the call
1413 * will be made on the main thread.
1414 * If no result is required, pass null.
1416 public void evaluateJavaScript(String script, JavaScriptCallback callback) {
1417 if (mNativeContentViewCore == 0) return;
1418 nativeEvaluateJavaScript(mNativeContentViewCore, script, callback, false);
1422 * Injects the passed Javascript code in the current page and evaluates it.
1423 * If there is no page existing, a new one will be created.
1425 * @param script The Javascript to execute.
1427 public void evaluateJavaScriptEvenIfNotYetNavigated(String script) {
1428 if (mNativeContentViewCore == 0) return;
1429 nativeEvaluateJavaScript(mNativeContentViewCore, script, null, true);
1433 * To be called when the ContentView is shown.
1435 public void onShow() {
1436 assert mNativeContentViewCore != 0;
1437 nativeOnShow(mNativeContentViewCore);
1438 setAccessibilityState(mAccessibilityManager.isEnabled());
1442 * @return The ID of the renderer process that backs this tab or
1443 * {@link #INVALID_RENDER_PROCESS_PID} if there is none.
1445 public int getCurrentRenderProcessId() {
1446 return nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1450 * To be called when the ContentView is hidden.
1452 public void onHide() {
1453 assert mNativeContentViewCore != 0;
1455 setInjectedAccessibility(false);
1456 nativeOnHide(mNativeContentViewCore);
1460 * Return the ContentSettings object used to retrieve the settings for this
1461 * ContentViewCore. For modifications, ChromeNativePreferences is to be used.
1462 * @return A ContentSettings object that can be used to retrieve this
1463 * ContentViewCore's settings.
1465 public ContentSettings getContentSettings() {
1466 return mContentSettings;
1469 private void hidePopups() {
1472 hideSelectActionBar();
1475 public void hideSelectActionBar() {
1476 if (mActionMode != null) {
1477 mActionMode.finish();
1482 public boolean isSelectActionBarShowing() {
1483 return mActionMode != null;
1486 private void resetGestureDetection() {
1487 if (mNativeContentViewCore == 0) return;
1488 nativeResetGestureDetection(mNativeContentViewCore);
1492 * @see View#onAttachedToWindow()
1494 @SuppressWarnings("javadoc")
1495 public void onAttachedToWindow() {
1496 setAccessibilityState(mAccessibilityManager.isEnabled());
1498 ScreenOrientationListener.getInstance().addObserver(this, mContext);
1499 GamepadList.onAttachedToWindow(mContext);
1503 * @see View#onDetachedFromWindow()
1505 @SuppressWarnings("javadoc")
1506 @SuppressLint("MissingSuperCall")
1507 public void onDetachedFromWindow() {
1508 setInjectedAccessibility(false);
1510 mZoomControlsDelegate.dismissZoomPicker();
1511 unregisterAccessibilityContentObserver();
1513 ScreenOrientationListener.getInstance().removeObserver(this);
1514 GamepadList.onDetachedFromWindow();
1518 * @see View#onVisibilityChanged(android.view.View, int)
1520 public void onVisibilityChanged(View changedView, int visibility) {
1521 if (visibility != View.VISIBLE) {
1522 mZoomControlsDelegate.dismissZoomPicker();
1527 * @see View#onCreateInputConnection(EditorInfo)
1529 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1530 if (!mImeAdapter.hasTextInputType()) {
1531 // Although onCheckIsTextEditor will return false in this case, the EditorInfo
1532 // is still used by the InputMethodService. Need to make sure the IME doesn't
1533 // enter fullscreen mode.
1534 outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
1536 mInputConnection = mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter,
1537 mEditable, outAttrs);
1538 return mInputConnection;
1542 public AdapterInputConnection getAdapterInputConnectionForTest() {
1543 return mInputConnection;
1547 public Editable getEditableForTest() {
1552 * @see View#onCheckIsTextEditor()
1554 public boolean onCheckIsTextEditor() {
1555 return mImeAdapter.hasTextInputType();
1559 * @see View#onConfigurationChanged(Configuration)
1561 @SuppressWarnings("javadoc")
1562 public void onConfigurationChanged(Configuration newConfig) {
1565 if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
1566 if (mNativeContentViewCore != 0) {
1567 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
1568 ImeAdapter.getTextInputTypeNone());
1570 mInputMethodManagerWrapper.restartInput(mContainerView);
1572 mContainerViewInternals.super_onConfigurationChanged(newConfig);
1574 // To request layout has side effect, but it seems OK as it only happen in
1575 // onConfigurationChange and layout has to be changed in most case.
1576 mContainerView.requestLayout();
1581 * @see View#onSizeChanged(int, int, int, int)
1583 @SuppressWarnings("javadoc")
1584 public void onSizeChanged(int wPix, int hPix, int owPix, int ohPix) {
1585 if (getViewportWidthPix() == wPix && getViewportHeightPix() == hPix) return;
1587 mViewportWidthPix = wPix;
1588 mViewportHeightPix = hPix;
1589 if (mNativeContentViewCore != 0) {
1590 nativeWasResized(mNativeContentViewCore);
1593 updateAfterSizeChanged();
1597 * Called when the underlying surface the compositor draws to changes size.
1598 * This may be larger than the viewport size.
1600 public void onPhysicalBackingSizeChanged(int wPix, int hPix) {
1601 if (mPhysicalBackingWidthPix == wPix && mPhysicalBackingHeightPix == hPix) return;
1603 mPhysicalBackingWidthPix = wPix;
1604 mPhysicalBackingHeightPix = hPix;
1606 if (mNativeContentViewCore != 0) {
1607 nativeWasResized(mNativeContentViewCore);
1612 * Called when the amount the surface is overdrawing off the bottom has changed.
1613 * @param overdrawHeightPix The overdraw height.
1615 public void onOverdrawBottomHeightChanged(int overdrawHeightPix) {
1616 if (mOverdrawBottomHeightPix == overdrawHeightPix) return;
1618 mOverdrawBottomHeightPix = overdrawHeightPix;
1620 if (mNativeContentViewCore != 0) {
1621 nativeWasResized(mNativeContentViewCore);
1625 private void updateAfterSizeChanged() {
1626 mPopupZoomer.hide(false);
1628 // Execute a delayed form focus operation because the OSK was brought
1630 if (!mFocusPreOSKViewportRect.isEmpty()) {
1631 Rect rect = new Rect();
1632 getContainerView().getWindowVisibleDisplayFrame(rect);
1633 if (!rect.equals(mFocusPreOSKViewportRect)) {
1634 // Only assume the OSK triggered the onSizeChanged if width was preserved.
1635 if (rect.width() == mFocusPreOSKViewportRect.width()) {
1636 scrollFocusedEditableNodeIntoView();
1638 cancelRequestToScrollFocusedEditableNodeIntoView();
1643 private void cancelRequestToScrollFocusedEditableNodeIntoView() {
1644 // Zero-ing the rect will prevent |updateAfterSizeChanged()| from
1645 // issuing the delayed form focus event.
1646 mFocusPreOSKViewportRect.setEmpty();
1649 private void scrollFocusedEditableNodeIntoView() {
1650 if (mNativeContentViewCore == 0) return;
1651 // The native side keeps track of whether the zoom and scroll actually occurred. It is
1652 // more efficient to do it this way and sometimes fire an unnecessary message rather
1653 // than synchronize with the renderer and always have an additional message.
1654 nativeScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1658 * Selects the word around the caret, if any.
1659 * The caller can check if selection actually occurred by listening to OnSelectionChanged.
1661 public void selectWordAroundCaret() {
1662 if (mNativeContentViewCore == 0) return;
1663 nativeSelectWordAroundCaret(mNativeContentViewCore);
1667 * @see View#onWindowFocusChanged(boolean)
1669 public void onWindowFocusChanged(boolean hasWindowFocus) {
1670 if (!hasWindowFocus) resetGestureDetection();
1673 public void onFocusChanged(boolean gainFocus) {
1676 cancelRequestToScrollFocusedEditableNodeIntoView();
1678 if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1682 * @see View#onKeyUp(int, KeyEvent)
1684 public boolean onKeyUp(int keyCode, KeyEvent event) {
1685 if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1686 mPopupZoomer.hide(true);
1689 return mContainerViewInternals.super_onKeyUp(keyCode, event);
1693 * @see View#dispatchKeyEventPreIme(KeyEvent)
1695 public boolean dispatchKeyEventPreIme(KeyEvent event) {
1698 return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1705 * @see View#dispatchKeyEvent(KeyEvent)
1707 public boolean dispatchKeyEvent(KeyEvent event) {
1708 if (GamepadList.dispatchKeyEvent(event)) return true;
1709 if (getContentViewClient().shouldOverrideKeyEvent(event)) {
1710 return mContainerViewInternals.super_dispatchKeyEvent(event);
1713 if (mImeAdapter.dispatchKeyEvent(event)) return true;
1715 return mContainerViewInternals.super_dispatchKeyEvent(event);
1719 * @see View#onHoverEvent(MotionEvent)
1720 * Mouse move events are sent on hover enter, hover move and hover exit.
1721 * They are sent on hover exit because sometimes it acts as both a hover
1722 * move and hover exit.
1724 public boolean onHoverEvent(MotionEvent event) {
1725 TraceEvent.begin("onHoverEvent");
1726 MotionEvent offset = createOffsetMotionEvent(event);
1728 if (mBrowserAccessibilityManager != null) {
1729 return mBrowserAccessibilityManager.onHoverEvent(offset);
1732 // Work around Android bug where the x, y coordinates of a hover exit
1733 // event are incorrect when touch exploration is on.
1734 if (mTouchExplorationEnabled && offset.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
1738 mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1739 if (mNativeContentViewCore != 0) {
1740 nativeSendMouseMoveEvent(mNativeContentViewCore, offset.getEventTime(),
1741 offset.getX(), offset.getY());
1746 TraceEvent.end("onHoverEvent");
1751 * @see View#onGenericMotionEvent(MotionEvent)
1753 public boolean onGenericMotionEvent(MotionEvent event) {
1754 if (GamepadList.onGenericMotionEvent(event)) return true;
1755 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1756 switch (event.getAction()) {
1757 case MotionEvent.ACTION_SCROLL:
1758 if (mNativeContentViewCore == 0) return false;
1760 nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
1761 event.getX(), event.getY(),
1762 event.getAxisValue(MotionEvent.AXIS_VSCROLL));
1764 mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1765 // Send a delayed onMouseMove event so that we end
1766 // up hovering over the right position after the scroll.
1767 final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event);
1768 mFakeMouseMoveRunnable = new Runnable() {
1771 onHoverEvent(eventFakeMouseMove);
1772 eventFakeMouseMove.recycle();
1775 mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1779 return mContainerViewInternals.super_onGenericMotionEvent(event);
1783 * Sets the current amount to offset incoming touch events by. This is used to handle content
1784 * moving and not lining up properly with the android input system.
1785 * @param dx The X offset in pixels to shift touch events.
1786 * @param dy The Y offset in pixels to shift touch events.
1788 public void setCurrentMotionEventOffsets(float dx, float dy) {
1789 mCurrentTouchOffsetX = dx;
1790 mCurrentTouchOffsetY = dy;
1793 private MotionEvent createOffsetMotionEvent(MotionEvent src) {
1794 MotionEvent dst = MotionEvent.obtain(src);
1795 dst.offsetLocation(mCurrentTouchOffsetX, mCurrentTouchOffsetY);
1800 * @see View#scrollBy(int, int)
1801 * Currently the ContentView scrolling happens in the native side. In
1802 * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
1803 * are overridden, so that View's mScrollX and mScrollY will be unchanged at
1804 * (0, 0). This is critical for drawing ContentView correctly.
1806 public void scrollBy(int xPix, int yPix) {
1807 if (mNativeContentViewCore != 0) {
1808 nativeScrollBy(mNativeContentViewCore,
1809 SystemClock.uptimeMillis(), 0, 0, xPix, yPix);
1814 * @see View#scrollTo(int, int)
1816 public void scrollTo(int xPix, int yPix) {
1817 if (mNativeContentViewCore == 0) return;
1818 final float xCurrentPix = mRenderCoordinates.getScrollXPix();
1819 final float yCurrentPix = mRenderCoordinates.getScrollYPix();
1820 final float dxPix = xPix - xCurrentPix;
1821 final float dyPix = yPix - yCurrentPix;
1822 if (dxPix != 0 || dyPix != 0) {
1823 long time = SystemClock.uptimeMillis();
1824 nativeScrollBegin(mNativeContentViewCore, time,
1825 xCurrentPix, yCurrentPix, -dxPix, -dyPix);
1826 nativeScrollBy(mNativeContentViewCore,
1827 time, xCurrentPix, yCurrentPix, dxPix, dyPix);
1828 nativeScrollEnd(mNativeContentViewCore, time);
1832 // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1834 public int getNativeScrollXForTest() {
1835 return mRenderCoordinates.getScrollXPixInt();
1838 // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1840 public int getNativeScrollYForTest() {
1841 return mRenderCoordinates.getScrollYPixInt();
1845 * @see View#computeHorizontalScrollExtent()
1847 @SuppressWarnings("javadoc")
1848 public int computeHorizontalScrollExtent() {
1849 return mRenderCoordinates.getLastFrameViewportWidthPixInt();
1853 * @see View#computeHorizontalScrollOffset()
1855 @SuppressWarnings("javadoc")
1856 public int computeHorizontalScrollOffset() {
1857 return mRenderCoordinates.getScrollXPixInt();
1861 * @see View#computeHorizontalScrollRange()
1863 @SuppressWarnings("javadoc")
1864 public int computeHorizontalScrollRange() {
1865 return mRenderCoordinates.getContentWidthPixInt();
1869 * @see View#computeVerticalScrollExtent()
1871 @SuppressWarnings("javadoc")
1872 public int computeVerticalScrollExtent() {
1873 return mRenderCoordinates.getLastFrameViewportHeightPixInt();
1877 * @see View#computeVerticalScrollOffset()
1879 @SuppressWarnings("javadoc")
1880 public int computeVerticalScrollOffset() {
1881 return mRenderCoordinates.getScrollYPixInt();
1885 * @see View#computeVerticalScrollRange()
1887 @SuppressWarnings("javadoc")
1888 public int computeVerticalScrollRange() {
1889 return mRenderCoordinates.getContentHeightPixInt();
1892 // End FrameLayout overrides.
1895 * @see View#awakenScrollBars(int, boolean)
1897 @SuppressWarnings("javadoc")
1898 public boolean awakenScrollBars(int startDelay, boolean invalidate) {
1899 // For the default implementation of ContentView which draws the scrollBars on the native
1900 // side, calling this function may get us into a bad state where we keep drawing the
1901 // scrollBars, so disable it by always returning false.
1902 if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
1905 return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
1909 private void updateForTapOrPress(int type, float xPix, float yPix) {
1910 if (type != GestureEventType.SINGLE_TAP_CONFIRMED
1911 && type != GestureEventType.SINGLE_TAP_UP
1912 && type != GestureEventType.LONG_PRESS
1913 && type != GestureEventType.LONG_TAP) {
1917 if (mContainerView.isFocusable() && mContainerView.isFocusableInTouchMode()
1918 && !mContainerView.isFocused()) {
1919 mContainerView.requestFocus();
1922 if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
1924 mLastTapX = (int) xPix;
1925 mLastTapY = (int) yPix;
1927 if (type == GestureEventType.LONG_PRESS
1928 || type == GestureEventType.LONG_TAP) {
1929 getInsertionHandleController().allowAutomaticShowing();
1930 getSelectionHandleController().allowAutomaticShowing();
1932 if (mSelectionEditable) getInsertionHandleController().allowAutomaticShowing();
1937 * @return The x coordinate for the last point that a tap or press gesture was initiated from.
1939 public int getLastTapX() {
1944 * @return The y coordinate for the last point that a tap or press gesture was initiated from.
1946 public int getLastTapY() {
1950 public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
1951 mZoomControlsDelegate = zoomControlsDelegate;
1954 public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
1955 if (mNativeContentViewCore == 0) return;
1956 nativeSetMultiTouchZoomSupportEnabled(mNativeContentViewCore, supportsMultiTouchZoom);
1959 public void updateDoubleTapSupport(boolean supportsDoubleTap) {
1960 if (mNativeContentViewCore == 0) return;
1961 nativeSetDoubleTapSupportEnabled(mNativeContentViewCore, supportsDoubleTap);
1964 public void selectPopupMenuItems(int[] indices) {
1965 if (mNativeContentViewCore != 0) {
1966 nativeSelectPopupMenuItems(mNativeContentViewCore, indices);
1968 mSelectPopup = null;
1972 * Send the screen orientation value to the renderer.
1975 void sendOrientationChangeEvent(int orientation) {
1976 if (mNativeContentViewCore == 0) return;
1978 nativeSendOrientationChangeEvent(mNativeContentViewCore, orientation);
1982 * Register the delegate to be used when content can not be handled by
1983 * the rendering engine, and should be downloaded instead. This will replace
1984 * the current delegate, if any.
1985 * @param delegate An implementation of ContentViewDownloadDelegate.
1987 public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
1988 mDownloadDelegate = delegate;
1991 // Called by DownloadController.
1992 ContentViewDownloadDelegate getDownloadDelegate() {
1993 return mDownloadDelegate;
1996 private SelectionHandleController getSelectionHandleController() {
1997 if (mSelectionHandleController == null) {
1998 mSelectionHandleController = new SelectionHandleController(
1999 getContainerView(), mPositionObserver) {
2001 public void selectBetweenCoordinates(int x1, int y1, int x2, int y2) {
2002 if (mNativeContentViewCore != 0 && !(x1 == x2 && y1 == y2)) {
2003 nativeSelectBetweenCoordinates(mNativeContentViewCore,
2004 x1, y1 - mRenderCoordinates.getContentOffsetYPix(),
2005 x2, y2 - mRenderCoordinates.getContentOffsetYPix());
2010 public void showHandles(int startDir, int endDir) {
2011 final boolean wasShowing = isShowing();
2012 super.showHandles(startDir, endDir);
2013 if (!wasShowing || mActionMode == null) showSelectActionBar();
2018 mSelectionHandleController.hideAndDisallowAutomaticShowing();
2021 return mSelectionHandleController;
2024 private InsertionHandleController getInsertionHandleController() {
2025 if (mInsertionHandleController == null) {
2026 mInsertionHandleController = new InsertionHandleController(
2027 getContainerView(), mPositionObserver) {
2028 private static final int AVERAGE_LINE_HEIGHT = 14;
2031 public void setCursorPosition(int x, int y) {
2032 if (mNativeContentViewCore != 0) {
2033 nativeMoveCaret(mNativeContentViewCore,
2034 x, y - mRenderCoordinates.getContentOffsetYPix());
2039 public void paste() {
2040 mImeAdapter.paste();
2045 public int getLineHeight() {
2046 return (int) Math.ceil(
2047 mRenderCoordinates.fromLocalCssToPix(AVERAGE_LINE_HEIGHT));
2051 public void showHandle() {
2056 mInsertionHandleController.hideAndDisallowAutomaticShowing();
2059 return mInsertionHandleController;
2063 public InsertionHandleController getInsertionHandleControllerForTest() {
2064 return mInsertionHandleController;
2068 public SelectionHandleController getSelectionHandleControllerForTest() {
2069 return mSelectionHandleController;
2072 private void updateHandleScreenPositions() {
2073 if (isSelectionHandleShowing()) {
2074 mSelectionHandleController.setStartHandlePosition(
2075 mStartHandlePoint.getXPix(), mStartHandlePoint.getYPix());
2076 mSelectionHandleController.setEndHandlePosition(
2077 mEndHandlePoint.getXPix(), mEndHandlePoint.getYPix());
2080 if (isInsertionHandleShowing()) {
2081 mInsertionHandleController.setHandlePosition(
2082 mInsertionHandlePoint.getXPix(), mInsertionHandlePoint.getYPix());
2086 private void hideHandles() {
2087 if (mSelectionHandleController != null) {
2088 mSelectionHandleController.hideAndDisallowAutomaticShowing();
2090 if (mInsertionHandleController != null) {
2091 mInsertionHandleController.hideAndDisallowAutomaticShowing();
2093 mPositionObserver.removeListener(mPositionListener);
2096 private void showSelectActionBar() {
2097 if (mActionMode != null) {
2098 mActionMode.invalidate();
2102 // Start a new action mode with a SelectActionModeCallback.
2103 SelectActionModeCallback.ActionHandler actionHandler =
2104 new SelectActionModeCallback.ActionHandler() {
2106 public void selectAll() {
2107 mImeAdapter.selectAll();
2116 public void copy() {
2121 public void paste() {
2122 mImeAdapter.paste();
2126 public void share() {
2127 final String query = getSelectedText();
2128 if (TextUtils.isEmpty(query)) return;
2130 Intent send = new Intent(Intent.ACTION_SEND);
2131 send.setType("text/plain");
2132 send.putExtra(Intent.EXTRA_TEXT, query);
2134 Intent i = Intent.createChooser(send, getContext().getString(
2135 R.string.actionbar_share));
2136 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2137 getContext().startActivity(i);
2138 } catch (android.content.ActivityNotFoundException ex) {
2139 // If no app handles it, do nothing.
2144 public void search() {
2145 final String query = getSelectedText();
2146 if (TextUtils.isEmpty(query)) return;
2148 // See if ContentViewClient wants to override
2149 if (getContentViewClient().doesPerformWebSearch()) {
2150 getContentViewClient().performWebSearch(query);
2154 Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
2155 i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2156 i.putExtra(SearchManager.QUERY, query);
2157 i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
2158 if (!(getContext() instanceof Activity)) {
2159 i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2162 getContext().startActivity(i);
2163 } catch (android.content.ActivityNotFoundException ex) {
2164 // If no app handles it, do nothing.
2169 public boolean isSelectionPassword() {
2170 return mImeAdapter.isSelectionPassword();
2174 public boolean isSelectionEditable() {
2175 return mSelectionEditable;
2179 public void onDestroyActionMode() {
2181 if (mUnselectAllOnActionModeDismiss) mImeAdapter.unselect();
2182 getContentViewClient().onContextualActionBarHidden();
2186 public boolean isShareAvailable() {
2187 Intent intent = new Intent(Intent.ACTION_SEND);
2188 intent.setType("text/plain");
2189 return getContext().getPackageManager().queryIntentActivities(intent,
2190 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2194 public boolean isWebSearchAvailable() {
2195 if (getContentViewClient().doesPerformWebSearch()) return true;
2196 Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
2197 intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2198 return getContext().getPackageManager().queryIntentActivities(intent,
2199 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2203 // On ICS, startActionMode throws an NPE when getParent() is null.
2204 if (mContainerView.getParent() != null) {
2205 mActionMode = mContainerView.startActionMode(
2206 getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler,
2207 nativeIsIncognito(mNativeContentViewCore)));
2209 mUnselectAllOnActionModeDismiss = true;
2210 if (mActionMode == null) {
2211 // There is no ActionMode, so remove the selection.
2212 mImeAdapter.unselect();
2214 getContentViewClient().onContextualActionBarShown();
2218 public boolean getUseDesktopUserAgent() {
2219 if (mNativeContentViewCore != 0) {
2220 return nativeGetUseDesktopUserAgent(mNativeContentViewCore);
2226 * Set whether or not we're using a desktop user agent for the currently loaded page.
2227 * @param override If true, use a desktop user agent. Use a mobile one otherwise.
2228 * @param reloadOnChange Reload the page if the UA has changed.
2230 public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange) {
2231 if (mNativeContentViewCore != 0) {
2232 nativeSetUseDesktopUserAgent(mNativeContentViewCore, override, reloadOnChange);
2236 public void clearSslPreferences() {
2237 if (mNativeContentViewCore != 0) nativeClearSslPreferences(mNativeContentViewCore);
2240 private boolean isSelectionHandleShowing() {
2241 return mSelectionHandleController != null && mSelectionHandleController.isShowing();
2244 private boolean isInsertionHandleShowing() {
2245 return mInsertionHandleController != null && mInsertionHandleController.isShowing();
2248 // Makes the insertion/selection handles invisible. They will fade back in shortly after the
2249 // last call to scheduleTextHandleFadeIn (or temporarilyHideTextHandles).
2250 private void temporarilyHideTextHandles() {
2251 if (isSelectionHandleShowing() && !mSelectionHandleController.isDragging()) {
2252 mSelectionHandleController.setHandleVisibility(HandleView.INVISIBLE);
2254 if (isInsertionHandleShowing() && !mInsertionHandleController.isDragging()) {
2255 mInsertionHandleController.setHandleVisibility(HandleView.INVISIBLE);
2257 scheduleTextHandleFadeIn();
2260 private boolean allowTextHandleFadeIn() {
2261 if (mTouchScrollInProgress) return false;
2263 if (mPopupZoomer.isShowing()) return false;
2268 // Cancels any pending fade in and schedules a new one.
2269 private void scheduleTextHandleFadeIn() {
2270 if (!isInsertionHandleShowing() && !isSelectionHandleShowing()) return;
2272 if (mDeferredHandleFadeInRunnable == null) {
2273 mDeferredHandleFadeInRunnable = new Runnable() {
2276 if (!allowTextHandleFadeIn()) {
2277 // Delay fade in until it is allowed.
2278 scheduleTextHandleFadeIn();
2280 if (isSelectionHandleShowing()) {
2281 mSelectionHandleController.beginHandleFadeIn();
2283 if (isInsertionHandleShowing()) {
2284 mInsertionHandleController.beginHandleFadeIn();
2291 mContainerView.removeCallbacks(mDeferredHandleFadeInRunnable);
2292 mContainerView.postDelayed(mDeferredHandleFadeInRunnable, TEXT_HANDLE_FADE_IN_DELAY);
2296 * Shows the IME if the focused widget could accept text input.
2298 public void showImeIfNeeded() {
2299 if (mNativeContentViewCore != 0) nativeShowImeIfNeeded(mNativeContentViewCore);
2303 * Hides the IME if the containerView is the active view for IME.
2305 public void hideImeIfNeeded() {
2306 // Hide input method window from the current view synchronously
2307 // because ImeAdapter does so asynchronouly with a delay, and
2308 // by the time when ImeAdapter dismisses the input, the
2309 // containerView may have lost focus.
2310 // We cannot trust ContentViewClient#onImeStateChangeRequested to
2311 // hide the input window because it has an empty default implementation.
2312 // So we need to explicitly hide the input method window here.
2313 if (mInputMethodManagerWrapper.isActive(mContainerView)) {
2314 mInputMethodManagerWrapper.hideSoftInputFromWindow(
2315 mContainerView.getWindowToken(), 0, null);
2317 getContentViewClient().onImeStateChangeRequested(false);
2320 @SuppressWarnings("unused")
2322 private void updateFrameInfo(
2323 float scrollOffsetX, float scrollOffsetY,
2324 float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
2325 float contentWidth, float contentHeight,
2326 float viewportWidth, float viewportHeight,
2327 float controlsOffsetYCss, float contentOffsetYCss,
2328 float overdrawBottomHeightCss) {
2329 TraceEvent.instant("ContentViewCore:updateFrameInfo");
2330 // Adjust contentWidth/Height to be always at least as big as
2331 // the actual viewport (as set by onSizeChanged).
2332 final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2333 contentWidth = Math.max(contentWidth,
2334 mViewportWidthPix / (deviceScale * pageScaleFactor));
2335 contentHeight = Math.max(contentHeight,
2336 mViewportHeightPix / (deviceScale * pageScaleFactor));
2337 final float contentOffsetYPix = mRenderCoordinates.fromDipToPix(contentOffsetYCss);
2339 final boolean contentSizeChanged =
2340 contentWidth != mRenderCoordinates.getContentWidthCss()
2341 || contentHeight != mRenderCoordinates.getContentHeightCss();
2342 final boolean scaleLimitsChanged =
2343 minPageScaleFactor != mRenderCoordinates.getMinPageScaleFactor()
2344 || maxPageScaleFactor != mRenderCoordinates.getMaxPageScaleFactor();
2345 final boolean pageScaleChanged =
2346 pageScaleFactor != mRenderCoordinates.getPageScaleFactor();
2347 final boolean scrollChanged =
2349 || scrollOffsetX != mRenderCoordinates.getScrollX()
2350 || scrollOffsetY != mRenderCoordinates.getScrollY();
2351 final boolean contentOffsetChanged =
2352 contentOffsetYPix != mRenderCoordinates.getContentOffsetYPix();
2354 final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
2355 final boolean needUpdateZoomControls = scaleLimitsChanged || scrollChanged;
2356 final boolean needTemporarilyHideHandles = scrollChanged;
2358 if (needHidePopupZoomer) mPopupZoomer.hide(true);
2360 if (scrollChanged) {
2361 mContainerViewInternals.onScrollChanged(
2362 (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetX),
2363 (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetY),
2364 (int) mRenderCoordinates.getScrollXPix(),
2365 (int) mRenderCoordinates.getScrollYPix());
2368 mRenderCoordinates.updateFrameInfo(
2369 scrollOffsetX, scrollOffsetY,
2370 contentWidth, contentHeight,
2371 viewportWidth, viewportHeight,
2372 pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
2375 if (scrollChanged || contentOffsetChanged) {
2376 for (mGestureStateListenersIterator.rewind();
2377 mGestureStateListenersIterator.hasNext();) {
2378 mGestureStateListenersIterator.next().onScrollOffsetOrExtentChanged(
2379 computeVerticalScrollOffset(),
2380 computeVerticalScrollExtent());
2384 if (needTemporarilyHideHandles) temporarilyHideTextHandles();
2385 if (needUpdateZoomControls) mZoomControlsDelegate.updateZoomControls();
2386 if (contentOffsetChanged) updateHandleScreenPositions();
2388 // Update offsets for fullscreen.
2389 final float controlsOffsetPix = controlsOffsetYCss * deviceScale;
2390 final float overdrawBottomHeightPix = overdrawBottomHeightCss * deviceScale;
2391 getContentViewClient().onOffsetsForFullscreenChanged(
2392 controlsOffsetPix, contentOffsetYPix, overdrawBottomHeightPix);
2394 if (mBrowserAccessibilityManager != null) {
2395 mBrowserAccessibilityManager.notifyFrameInfoInitialized();
2400 private void updateImeAdapter(long nativeImeAdapterAndroid, int textInputType,
2401 String text, int selectionStart, int selectionEnd,
2402 int compositionStart, int compositionEnd, boolean showImeIfNeeded,
2403 boolean isNonImeChange) {
2405 mSelectionEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
2407 mImeAdapter.updateKeyboardVisibility(
2408 nativeImeAdapterAndroid, textInputType, showImeIfNeeded);
2410 if (mInputConnection != null) {
2411 mInputConnection.updateState(text, selectionStart, selectionEnd, compositionStart,
2412 compositionEnd, isNonImeChange);
2415 if (mActionMode != null) mActionMode.invalidate();
2419 @SuppressWarnings("unused")
2421 private void setTitle(String title) {
2422 getContentViewClient().onUpdateTitle(title);
2426 * Called (from native) when the <select> popup needs to be shown.
2427 * @param items Items to show.
2428 * @param enabled POPUP_ITEM_TYPEs for items.
2429 * @param multiple Whether the popup menu should support multi-select.
2430 * @param selectedIndices Indices of selected items.
2432 @SuppressWarnings("unused")
2434 private void showSelectPopup(Rect bounds, String[] items, int[] enabled, boolean multiple,
2435 int[] selectedIndices) {
2436 if (mContainerView.getParent() == null || mContainerView.getVisibility() != View.VISIBLE) {
2437 selectPopupMenuItems(null);
2441 assert items.length == enabled.length;
2442 List<SelectPopupItem> popupItems = new ArrayList<SelectPopupItem>();
2443 for (int i = 0; i < items.length; i++) {
2444 popupItems.add(new SelectPopupItem(items[i], enabled[i]));
2447 if (DeviceUtils.isTablet(mContext) && !multiple) {
2448 mSelectPopup = new SelectPopupDropdown(this, popupItems, bounds, selectedIndices);
2450 mSelectPopup = new SelectPopupDialog(this, popupItems, multiple, selectedIndices);
2452 mSelectPopup.show();
2456 * Called when the <select> popup needs to be hidden.
2459 private void hideSelectPopup() {
2460 if (mSelectPopup != null) mSelectPopup.hide();
2464 * @return The visible select popup being shown.
2466 public SelectPopup getSelectPopupForTest() {
2467 return mSelectPopup;
2470 @SuppressWarnings("unused")
2472 private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
2473 mPopupZoomer.setBitmap(zoomedBitmap);
2474 mPopupZoomer.show(targetRect);
2475 temporarilyHideTextHandles();
2478 @SuppressWarnings("unused")
2480 private TouchEventSynthesizer createTouchEventSynthesizer() {
2481 return new TouchEventSynthesizer(this);
2484 @SuppressWarnings("unused")
2486 private void onSelectionChanged(String text) {
2487 mLastSelectedText = text;
2488 getContentViewClient().onSelectionChanged(text);
2491 @SuppressWarnings("unused")
2493 private void showSelectionHandlesAutomatically() {
2494 getSelectionHandleController().allowAutomaticShowing();
2497 @SuppressWarnings("unused")
2499 private void onSelectionBoundsChanged(Rect anchorRectDip, int anchorDir, Rect focusRectDip,
2500 int focusDir, boolean isAnchorFirst) {
2501 // All coordinates are in DIP.
2502 int x1 = anchorRectDip.left;
2503 int y1 = anchorRectDip.bottom;
2504 int x2 = focusRectDip.left;
2505 int y2 = focusRectDip.bottom;
2507 if (x1 != x2 || y1 != y2 ||
2508 (mSelectionHandleController != null && mSelectionHandleController.isDragging())) {
2509 if (mInsertionHandleController != null) {
2510 mInsertionHandleController.hide();
2512 if (isAnchorFirst) {
2513 mStartHandlePoint.setLocalDip(x1, y1);
2514 mEndHandlePoint.setLocalDip(x2, y2);
2516 mStartHandlePoint.setLocalDip(x2, y2);
2517 mEndHandlePoint.setLocalDip(x1, y1);
2520 boolean wereSelectionHandlesShowing = getSelectionHandleController().isShowing();
2522 getSelectionHandleController().onSelectionChanged(anchorDir, focusDir);
2523 updateHandleScreenPositions();
2524 mHasSelection = true;
2526 if (!wereSelectionHandlesShowing && getSelectionHandleController().isShowing()) {
2527 // TODO(cjhopman): Remove this when there is a better signal that long press caused
2528 // a selection. See http://crbug.com/150151.
2529 mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2533 mUnselectAllOnActionModeDismiss = false;
2534 hideSelectActionBar();
2535 if (x1 != 0 && y1 != 0 && mSelectionEditable) {
2536 // Selection is a caret, and a text field is focused.
2537 if (mSelectionHandleController != null) {
2538 mSelectionHandleController.hide();
2540 mInsertionHandlePoint.setLocalDip(x1, y1);
2542 getInsertionHandleController().onCursorPositionChanged();
2543 updateHandleScreenPositions();
2544 if (mInputMethodManagerWrapper.isWatchingCursor(mContainerView)) {
2545 final int xPix = (int) mInsertionHandlePoint.getXPix();
2546 final int yPix = (int) mInsertionHandlePoint.getYPix();
2547 mInputMethodManagerWrapper.updateCursor(
2548 mContainerView, xPix, yPix, xPix, yPix);
2552 if (mSelectionHandleController != null) {
2553 mSelectionHandleController.hideAndDisallowAutomaticShowing();
2555 if (mInsertionHandleController != null) {
2556 mInsertionHandleController.hideAndDisallowAutomaticShowing();
2559 mHasSelection = false;
2561 if (isSelectionHandleShowing() || isInsertionHandleShowing()) {
2562 mPositionObserver.addListener(mPositionListener);
2566 @SuppressWarnings("unused")
2568 private static void onEvaluateJavaScriptResult(
2569 String jsonResult, JavaScriptCallback callback) {
2570 callback.handleJavaScriptResult(jsonResult);
2573 @SuppressWarnings("unused")
2575 private void showPastePopup(int xDip, int yDip) {
2576 mInsertionHandlePoint.setLocalDip(xDip, yDip);
2577 getInsertionHandleController().showHandle();
2578 updateHandleScreenPositions();
2579 getInsertionHandleController().showHandleWithPastePopup();
2582 @SuppressWarnings("unused")
2584 private void onRenderProcessChange() {
2589 * Attaches the native ImeAdapter object to the java ImeAdapter to allow communication via JNI.
2591 public void attachImeAdapter() {
2592 if (mImeAdapter != null && mNativeContentViewCore != 0) {
2593 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2598 * @see View#hasFocus()
2601 public boolean hasFocus() {
2602 return mContainerView.hasFocus();
2606 * Checks whether the ContentViewCore can be zoomed in.
2608 * @return True if the ContentViewCore can be zoomed in.
2610 // This method uses the term 'zoom' for legacy reasons, but relates
2611 // to what chrome calls the 'page scale factor'.
2612 public boolean canZoomIn() {
2613 final float zoomInExtent = mRenderCoordinates.getMaxPageScaleFactor()
2614 - mRenderCoordinates.getPageScaleFactor();
2615 return zoomInExtent > ZOOM_CONTROLS_EPSILON;
2619 * Checks whether the ContentViewCore can be zoomed out.
2621 * @return True if the ContentViewCore can be zoomed out.
2623 // This method uses the term 'zoom' for legacy reasons, but relates
2624 // to what chrome calls the 'page scale factor'.
2625 public boolean canZoomOut() {
2626 final float zoomOutExtent = mRenderCoordinates.getPageScaleFactor()
2627 - mRenderCoordinates.getMinPageScaleFactor();
2628 return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
2632 * Zooms in the ContentViewCore by 25% (or less if that would result in
2633 * zooming in more than possible).
2635 * @return True if there was a zoom change, false otherwise.
2637 // This method uses the term 'zoom' for legacy reasons, but relates
2638 // to what chrome calls the 'page scale factor'.
2639 public boolean zoomIn() {
2643 return pinchByDelta(1.25f);
2647 * Zooms out the ContentViewCore by 20% (or less if that would result in
2648 * zooming out more than possible).
2650 * @return True if there was a zoom change, false otherwise.
2652 // This method uses the term 'zoom' for legacy reasons, but relates
2653 // to what chrome calls the 'page scale factor'.
2654 public boolean zoomOut() {
2655 if (!canZoomOut()) {
2658 return pinchByDelta(0.8f);
2662 * Resets the zoom factor of the ContentViewCore.
2664 * @return True if there was a zoom change, false otherwise.
2666 // This method uses the term 'zoom' for legacy reasons, but relates
2667 // to what chrome calls the 'page scale factor'.
2668 public boolean zoomReset() {
2669 // The page scale factor is initialized to mNativeMinimumScale when
2670 // the page finishes loading. Thus sets it back to mNativeMinimumScale.
2671 if (!canZoomOut()) return false;
2672 return pinchByDelta(
2673 mRenderCoordinates.getMinPageScaleFactor()
2674 / mRenderCoordinates.getPageScaleFactor());
2678 * Simulate a pinch zoom gesture.
2680 * @param delta the factor by which the current page scale should be multiplied by.
2681 * @return whether the gesture was sent.
2683 public boolean pinchByDelta(float delta) {
2684 if (mNativeContentViewCore == 0) return false;
2686 long timeMs = SystemClock.uptimeMillis();
2687 int xPix = getViewportWidthPix() / 2;
2688 int yPix = getViewportHeightPix() / 2;
2690 nativePinchBegin(mNativeContentViewCore, timeMs, xPix, yPix);
2691 nativePinchBy(mNativeContentViewCore, timeMs, xPix, yPix, delta);
2692 nativePinchEnd(mNativeContentViewCore, timeMs);
2698 * Invokes the graphical zoom picker widget for this ContentView.
2700 public void invokeZoomPicker() {
2701 mZoomControlsDelegate.invokeZoomPicker();
2705 * Enables or disables inspection of JavaScript objects added via
2706 * {@link #addJavascriptInterface(Object, String)} by means of Object.keys() method and
2707 * "for .. in" loop. Being able to inspect JavaScript objects is useful
2708 * when debugging hybrid Android apps, but can't be enabled for legacy applications due
2709 * to compatibility risks.
2711 * @param allow Whether to allow JavaScript objects inspection.
2713 public void setAllowJavascriptInterfacesInspection(boolean allow) {
2714 nativeSetAllowJavascriptInterfacesInspection(mNativeContentViewCore, allow);
2718 * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
2719 * and automatically pass in {@link JavascriptInterface} as the required annotation.
2721 * @param object The Java object to inject into the ContentViewCore's JavaScript context. Null
2722 * values are ignored.
2723 * @param name The name used to expose the instance in JavaScript.
2725 public void addJavascriptInterface(Object object, String name) {
2726 addPossiblyUnsafeJavascriptInterface(object, name, JavascriptInterface.class);
2730 * This method injects the supplied Java object into the ContentViewCore.
2731 * The object is injected into the JavaScript context of the main frame,
2732 * using the supplied name. This allows the Java object to be accessed from
2733 * JavaScript. Note that that injected objects will not appear in
2734 * JavaScript until the page is next (re)loaded. For example:
2735 * <pre> view.addJavascriptInterface(new Object(), "injectedObject");
2736 * view.loadData("<!DOCTYPE html><title></title>", "text/html", null);
2737 * view.loadUrl("javascript:alert(injectedObject.toString())");</pre>
2738 * <p><strong>IMPORTANT:</strong>
2740 * <li> addJavascriptInterface() can be used to allow JavaScript to control
2741 * the host application. This is a powerful feature, but also presents a
2742 * security risk. Use of this method in a ContentViewCore containing
2743 * untrusted content could allow an attacker to manipulate the host
2744 * application in unintended ways, executing Java code with the permissions
2745 * of the host application. Use extreme care when using this method in a
2746 * ContentViewCore which could contain untrusted content. Particular care
2747 * should be taken to avoid unintentional access to inherited methods, such
2748 * as {@link Object#getClass()}. To prevent access to inherited methods,
2749 * pass an annotation for {@code requiredAnnotation}. This will ensure
2750 * that only methods with {@code requiredAnnotation} are exposed to the
2751 * Javascript layer. {@code requiredAnnotation} will be passed to all
2752 * subsequently injected Java objects if any methods return an object. This
2753 * means the same restrictions (or lack thereof) will apply. Alternatively,
2754 * {@link #addJavascriptInterface(Object, String)} can be called, which
2755 * automatically uses the {@link JavascriptInterface} annotation.
2756 * <li> JavaScript interacts with Java objects on a private, background
2757 * thread of the ContentViewCore. Care is therefore required to maintain
2758 * thread safety.</li>
2761 * @param object The Java object to inject into the
2762 * ContentViewCore's JavaScript context. Null
2763 * values are ignored.
2764 * @param name The name used to expose the instance in
2766 * @param requiredAnnotation Restrict exposed methods to ones with this
2767 * annotation. If {@code null} all methods are
2771 public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
2772 Class<? extends Annotation> requiredAnnotation) {
2773 if (mNativeContentViewCore != 0 && object != null) {
2774 mJavaScriptInterfaces.put(name, object);
2775 nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation);
2780 * Removes a previously added JavaScript interface with the given name.
2782 * @param name The name of the interface to remove.
2784 public void removeJavascriptInterface(String name) {
2785 mJavaScriptInterfaces.remove(name);
2786 if (mNativeContentViewCore != 0) {
2787 nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2792 * Return the current scale of the ContentView.
2793 * @return The current page scale factor.
2795 public float getScale() {
2796 return mRenderCoordinates.getPageScaleFactor();
2800 * If the view is ready to draw contents to the screen. In hardware mode,
2801 * the initialization of the surface texture may not occur until after the
2802 * view has been added to the layout. This method will return {@code true}
2803 * once the texture is actually ready.
2805 public boolean isReady() {
2806 if (mNativeContentViewCore == 0) return false;
2807 return nativeIsRenderWidgetHostViewReady(mNativeContentViewCore);
2811 private void startContentIntent(String contentUrl) {
2812 getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2816 public void onAccessibilityStateChanged(boolean enabled) {
2817 setAccessibilityState(enabled);
2821 * Determines whether or not this ContentViewCore can handle this accessibility action.
2822 * @param action The action to perform.
2823 * @return Whether or not this action is supported.
2825 public boolean supportsAccessibilityAction(int action) {
2826 return mAccessibilityInjector.supportsAccessibilityAction(action);
2830 * Attempts to perform an accessibility action on the web content. If the accessibility action
2831 * cannot be processed, it returns {@code null}, allowing the caller to know to call the
2832 * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value.
2833 * Otherwise the return value from this method should be used.
2834 * @param action The action to perform.
2835 * @param arguments Optional action arguments.
2836 * @return Whether the action was performed or {@code null} if the call should be delegated to
2837 * the super {@link View} class.
2839 public boolean performAccessibilityAction(int action, Bundle arguments) {
2840 if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2841 return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2848 * Set the BrowserAccessibilityManager, used for native accessibility
2849 * (not script injection). This is only set when system accessibility
2851 * @param manager The new BrowserAccessibilityManager.
2853 public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
2854 mBrowserAccessibilityManager = manager;
2858 * Get the BrowserAccessibilityManager, used for native accessibility
2859 * (not script injection). This will return null when system accessibility
2861 * @return This view's BrowserAccessibilityManager.
2863 public BrowserAccessibilityManager getBrowserAccessibilityManager() {
2864 return mBrowserAccessibilityManager;
2868 * If native accessibility (not script injection) is enabled, and if this is
2869 * running on JellyBean or later, returns an AccessibilityNodeProvider that
2870 * implements native accessibility for this view. Returns null otherwise.
2871 * Lazily initializes native accessibility here if it's allowed.
2872 * @return The AccessibilityNodeProvider, if available, or null otherwise.
2874 public AccessibilityNodeProvider getAccessibilityNodeProvider() {
2875 if (mBrowserAccessibilityManager != null) {
2876 return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
2879 if (mNativeAccessibilityAllowed &&
2880 !mNativeAccessibilityEnabled &&
2881 mNativeContentViewCore != 0 &&
2882 Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
2883 mNativeAccessibilityEnabled = true;
2884 nativeSetAccessibilityEnabled(mNativeContentViewCore, true);
2891 * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
2893 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2894 // Note: this is only used by the script-injecting accessibility code.
2895 mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
2899 * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
2901 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
2902 // Note: this is only used by the script-injecting accessibility code.
2903 event.setClassName(this.getClass().getName());
2905 // Identify where the top-left of the screen currently points to.
2906 event.setScrollX(mRenderCoordinates.getScrollXPixInt());
2907 event.setScrollY(mRenderCoordinates.getScrollYPixInt());
2909 // The maximum scroll values are determined by taking the content dimensions and
2910 // subtracting off the actual dimensions of the ChromeView.
2911 int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt());
2912 int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt());
2913 event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0);
2915 // Setting the maximum scroll values requires API level 15 or higher.
2916 final int SDK_VERSION_REQUIRED_TO_SET_SCROLL = 15;
2917 if (Build.VERSION.SDK_INT >= SDK_VERSION_REQUIRED_TO_SET_SCROLL) {
2918 event.setMaxScrollX(maxScrollXPix);
2919 event.setMaxScrollY(maxScrollYPix);
2924 * Returns whether accessibility script injection is enabled on the device
2926 public boolean isDeviceAccessibilityScriptInjectionEnabled() {
2928 // On JellyBean and higher, native accessibility is the default so script
2929 // injection is only allowed if enabled via a flag.
2930 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&
2931 !CommandLine.getInstance().hasSwitch(
2932 ContentSwitches.ENABLE_ACCESSIBILITY_SCRIPT_INJECTION)) {
2936 if (!mContentSettings.getJavaScriptEnabled()) {
2940 int result = getContext().checkCallingOrSelfPermission(
2941 android.Manifest.permission.INTERNET);
2942 if (result != PackageManager.PERMISSION_GRANTED) {
2946 Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
2947 field.setAccessible(true);
2948 String accessibilityScriptInjection = (String) field.get(null);
2949 ContentResolver contentResolver = getContext().getContentResolver();
2951 if (mAccessibilityScriptInjectionObserver == null) {
2952 ContentObserver contentObserver = new ContentObserver(new Handler()) {
2954 public void onChange(boolean selfChange, Uri uri) {
2955 setAccessibilityState(mAccessibilityManager.isEnabled());
2958 contentResolver.registerContentObserver(
2959 Settings.Secure.getUriFor(accessibilityScriptInjection),
2962 mAccessibilityScriptInjectionObserver = contentObserver;
2965 return Settings.Secure.getInt(contentResolver, accessibilityScriptInjection, 0) == 1;
2966 } catch (NoSuchFieldException e) {
2967 // Do nothing, default to false.
2968 } catch (IllegalAccessException e) {
2969 // Do nothing, default to false.
2975 * Returns whether or not accessibility injection is being used.
2977 public boolean isInjectingAccessibilityScript() {
2978 return mAccessibilityInjector.accessibilityIsAvailable();
2982 * Returns true if accessibility is on and touch exploration is enabled.
2984 public boolean isTouchExplorationEnabled() {
2985 return mTouchExplorationEnabled;
2989 * Turns browser accessibility on or off.
2990 * If |state| is |false|, this turns off both native and injected accessibility.
2991 * Otherwise, if accessibility script injection is enabled, this will enable the injected
2992 * accessibility scripts. Native accessibility is enabled on demand.
2994 public void setAccessibilityState(boolean state) {
2996 setInjectedAccessibility(false);
2997 mNativeAccessibilityAllowed = false;
2998 mTouchExplorationEnabled = false;
3000 boolean useScriptInjection = isDeviceAccessibilityScriptInjectionEnabled();
3001 setInjectedAccessibility(useScriptInjection);
3002 mNativeAccessibilityAllowed = !useScriptInjection;
3003 mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
3008 * Enable or disable injected accessibility features
3010 public void setInjectedAccessibility(boolean enabled) {
3011 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
3012 mAccessibilityInjector.setScriptEnabled(enabled);
3016 * Stop any TTS notifications that are currently going on.
3018 public void stopCurrentAccessibilityNotifications() {
3019 mAccessibilityInjector.onPageLostFocus();
3023 * Inform WebKit that Fullscreen mode has been exited by the user.
3025 public void exitFullscreen() {
3026 if (mNativeContentViewCore != 0) nativeExitFullscreen(mNativeContentViewCore);
3030 * Changes whether hiding the top controls is enabled.
3032 * @param enableHiding Whether hiding the top controls should be enabled or not.
3033 * @param enableShowing Whether showing the top controls should be enabled or not.
3034 * @param animate Whether the transition should be animated or not.
3036 public void updateTopControlsState(boolean enableHiding, boolean enableShowing,
3038 if (mNativeContentViewCore != 0) {
3039 nativeUpdateTopControlsState(
3040 mNativeContentViewCore, enableHiding, enableShowing, animate);
3045 * Callback factory method for nativeGetNavigationHistory().
3048 private void addToNavigationHistory(Object history, int index, String url, String virtualUrl,
3049 String originalUrl, String title, Bitmap favicon) {
3050 NavigationEntry entry = new NavigationEntry(
3051 index, url, virtualUrl, originalUrl, title, favicon);
3052 ((NavigationHistory) history).addEntry(entry);
3056 * Get a copy of the navigation history of the view.
3058 public NavigationHistory getNavigationHistory() {
3059 NavigationHistory history = new NavigationHistory();
3060 if (mNativeContentViewCore != 0) {
3061 int currentIndex = nativeGetNavigationHistory(mNativeContentViewCore, history);
3062 history.setCurrentEntryIndex(currentIndex);
3068 public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
3069 NavigationHistory history = new NavigationHistory();
3070 if (mNativeContentViewCore != 0) {
3071 nativeGetDirectedNavigationHistory(
3072 mNativeContentViewCore, history, isForward, itemLimit);
3078 * @return The original request URL for the current navigation entry, or null if there is no
3081 public String getOriginalUrlForActiveNavigationEntry() {
3082 if (mNativeContentViewCore != 0) {
3083 return nativeGetOriginalUrlForActiveNavigationEntry(mNativeContentViewCore);
3089 * @return The cached copy of render positions and scales.
3091 public RenderCoordinates getRenderCoordinates() {
3092 return mRenderCoordinates;
3096 private static Rect createRect(int x, int y, int right, int bottom) {
3097 return new Rect(x, y, right, bottom);
3100 public void extractSmartClipData(int x, int y, int width, int height) {
3101 if (mNativeContentViewCore != 0) {
3102 x += mSmartClipOffsetX;
3103 y += mSmartClipOffsetY;
3104 nativeExtractSmartClipData(mNativeContentViewCore, x, y, width, height);
3109 * Set offsets for smart clip.
3111 * <p>This should be called if there is a viewport change introduced by,
3112 * e.g., show and hide of a location bar.
3114 * @param offsetX Offset for X position.
3115 * @param offsetY Offset for Y position.
3117 public void setSmartClipOffsets(int offsetX, int offsetY) {
3118 mSmartClipOffsetX = offsetX;
3119 mSmartClipOffsetY = offsetY;
3123 private void onSmartClipDataExtracted(String text, String html, Rect clipRect) {
3124 if (mSmartClipDataListener != null ) {
3125 mSmartClipDataListener.onSmartClipDataExtracted(text, html, clipRect);
3129 public void setSmartClipDataListener(SmartClipDataListener listener) {
3130 mSmartClipDataListener = listener;
3133 public void setBackgroundOpaque(boolean opaque) {
3134 if (mNativeContentViewCore != 0) {
3135 nativeSetBackgroundOpaque(mNativeContentViewCore, opaque);
3140 * Offer a long press gesture to the embedding View, primarily for WebView compatibility.
3142 * @return true if the embedder handled the event.
3144 private boolean offerLongPressToEmbedder() {
3145 return mContainerView.performLongClick();
3149 * Reset scroll and fling accounting, notifying listeners as appropriate.
3150 * This is useful as a failsafe when the input stream may have been interruped.
3152 private void resetScrollInProgress() {
3153 if (!isScrollInProgress()) return;
3155 final boolean touchScrollInProgress = mTouchScrollInProgress;
3156 final int potentiallyActiveFlingCount = mPotentiallyActiveFlingCount;
3158 mTouchScrollInProgress = false;
3159 mPotentiallyActiveFlingCount = 0;
3161 if (touchScrollInProgress) updateGestureStateListener(GestureEventType.SCROLL_END);
3162 if (potentiallyActiveFlingCount > 0) updateGestureStateListener(GestureEventType.FLING_END);
3165 private native long nativeInit(long webContentsPtr,
3166 long viewAndroidPtr, long windowAndroidPtr, HashSet<Object> retainedObjectSet);
3169 private ContentVideoViewClient getContentVideoViewClient() {
3170 return getContentViewClient().getContentVideoViewClient();
3174 private boolean shouldBlockMediaRequest(String url) {
3175 return getContentViewClient().shouldBlockMediaRequest(url);
3179 private void onNativeFlingStopped() {
3180 // Note that mTouchScrollInProgress should normally be false at this
3181 // point, but we reset it anyway as another failsafe.
3182 mTouchScrollInProgress = false;
3183 if (mPotentiallyActiveFlingCount <= 0) return;
3184 mPotentiallyActiveFlingCount--;
3185 updateGestureStateListener(GestureEventType.FLING_END);
3189 public void onScreenOrientationChanged(int orientation) {
3190 sendOrientationChangeEvent(orientation);
3193 private native WebContents nativeGetWebContentsAndroid(long nativeContentViewCoreImpl);
3195 private native void nativeOnJavaContentViewCoreDestroyed(long nativeContentViewCoreImpl);
3197 private native void nativeLoadUrl(
3198 long nativeContentViewCoreImpl,
3204 int uaOverrideOption,
3205 String extraHeaders,
3207 String baseUrlForDataUrl,
3208 String virtualUrlForDataUrl,
3209 boolean canLoadLocalResources,
3210 boolean isRendererInitiated);
3212 private native String nativeGetURL(long nativeContentViewCoreImpl);
3214 private native void nativeShowInterstitialPage(
3215 long nativeContentViewCoreImpl, String url, long nativeInterstitialPageDelegateAndroid);
3216 private native boolean nativeIsShowingInterstitialPage(long nativeContentViewCoreImpl);
3218 private native boolean nativeIsIncognito(long nativeContentViewCoreImpl);
3220 private native void nativeSetFocus(long nativeContentViewCoreImpl, boolean focused);
3222 private native void nativeSendOrientationChangeEvent(
3223 long nativeContentViewCoreImpl, int orientation);
3225 // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
3226 private native boolean nativeOnTouchEvent(
3227 long nativeContentViewCoreImpl, MotionEvent event,
3228 long timeMs, int action, int pointerCount, int historySize, int actionIndex,
3229 float x0, float y0, float x1, float y1,
3230 int pointerId0, int pointerId1,
3231 float touchMajor0, float touchMajor1,
3232 float rawX, float rawY,
3233 int androidToolType0, int androidToolType1, int androidButtonState);
3235 private native int nativeSendMouseMoveEvent(
3236 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3238 private native int nativeSendMouseWheelEvent(
3239 long nativeContentViewCoreImpl, long timeMs, float x, float y, float verticalAxis);
3241 private native void nativeScrollBegin(
3242 long nativeContentViewCoreImpl, long timeMs, float x, float y, float hintX,
3245 private native void nativeScrollEnd(long nativeContentViewCoreImpl, long timeMs);
3247 private native void nativeScrollBy(
3248 long nativeContentViewCoreImpl, long timeMs, float x, float y,
3249 float deltaX, float deltaY);
3251 private native void nativeFlingStart(
3252 long nativeContentViewCoreImpl, long timeMs, float x, float y, float vx, float vy);
3254 private native void nativeFlingCancel(long nativeContentViewCoreImpl, long timeMs);
3256 private native void nativeSingleTap(
3257 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3259 private native void nativeDoubleTap(
3260 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3262 private native void nativeLongPress(
3263 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3265 private native void nativePinchBegin(
3266 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3268 private native void nativePinchEnd(long nativeContentViewCoreImpl, long timeMs);
3270 private native void nativePinchBy(long nativeContentViewCoreImpl, long timeMs,
3271 float anchorX, float anchorY, float deltaScale);
3273 private native void nativeSelectBetweenCoordinates(
3274 long nativeContentViewCoreImpl, float x1, float y1, float x2, float y2);
3276 private native void nativeMoveCaret(long nativeContentViewCoreImpl, float x, float y);
3278 private native void nativeResetGestureDetection(long nativeContentViewCoreImpl);
3279 private native void nativeSetDoubleTapSupportEnabled(
3280 long nativeContentViewCoreImpl, boolean enabled);
3281 private native void nativeSetMultiTouchZoomSupportEnabled(
3282 long nativeContentViewCoreImpl, boolean enabled);
3284 private native void nativeLoadIfNecessary(long nativeContentViewCoreImpl);
3285 private native void nativeRequestRestoreLoad(long nativeContentViewCoreImpl);
3287 private native void nativeReload(long nativeContentViewCoreImpl, boolean checkForRepost);
3288 private native void nativeReloadIgnoringCache(
3289 long nativeContentViewCoreImpl, boolean checkForRepost);
3291 private native void nativeCancelPendingReload(long nativeContentViewCoreImpl);
3293 private native void nativeContinuePendingReload(long nativeContentViewCoreImpl);
3295 private native void nativeSelectPopupMenuItems(long nativeContentViewCoreImpl, int[] indices);
3297 private native void nativeScrollFocusedEditableNodeIntoView(long nativeContentViewCoreImpl);
3299 private native void nativeSelectWordAroundCaret(long nativeContentViewCoreImpl);
3301 private native void nativeClearHistory(long nativeContentViewCoreImpl);
3303 private native void nativeAddStyleSheetByURL(long nativeContentViewCoreImpl,
3304 String stylesheetUrl);
3306 private native void nativeEvaluateJavaScript(long nativeContentViewCoreImpl,
3307 String script, JavaScriptCallback callback, boolean startRenderer);
3309 private native long nativeGetNativeImeAdapter(long nativeContentViewCoreImpl);
3311 private native int nativeGetCurrentRenderProcessId(long nativeContentViewCoreImpl);
3313 private native int nativeGetBackgroundColor(long nativeContentViewCoreImpl);
3315 private native void nativeOnShow(long nativeContentViewCoreImpl);
3316 private native void nativeOnHide(long nativeContentViewCoreImpl);
3318 private native void nativeSetUseDesktopUserAgent(long nativeContentViewCoreImpl,
3319 boolean enabled, boolean reloadOnChange);
3320 private native boolean nativeGetUseDesktopUserAgent(long nativeContentViewCoreImpl);
3322 private native void nativeClearSslPreferences(long nativeContentViewCoreImpl);
3324 private native void nativeSetAllowJavascriptInterfacesInspection(
3325 long nativeContentViewCoreImpl, boolean allow);
3327 private native void nativeAddJavascriptInterface(long nativeContentViewCoreImpl, Object object,
3328 String name, Class requiredAnnotation);
3330 private native void nativeRemoveJavascriptInterface(long nativeContentViewCoreImpl,
3333 private native int nativeGetNavigationHistory(long nativeContentViewCoreImpl, Object context);
3334 private native void nativeGetDirectedNavigationHistory(long nativeContentViewCoreImpl,
3335 Object context, boolean isForward, int maxEntries);
3336 private native String nativeGetOriginalUrlForActiveNavigationEntry(
3337 long nativeContentViewCoreImpl);
3339 private native void nativeWasResized(long nativeContentViewCoreImpl);
3341 private native boolean nativeIsRenderWidgetHostViewReady(long nativeContentViewCoreImpl);
3343 private native void nativeExitFullscreen(long nativeContentViewCoreImpl);
3344 private native void nativeUpdateTopControlsState(long nativeContentViewCoreImpl,
3345 boolean enableHiding, boolean enableShowing, boolean animate);
3347 private native void nativeShowImeIfNeeded(long nativeContentViewCoreImpl);
3349 private native void nativeSetAccessibilityEnabled(
3350 long nativeContentViewCoreImpl, boolean enabled);
3352 private native void nativeExtractSmartClipData(long nativeContentViewCoreImpl,
3353 int x, int y, int w, int h);
3354 private native void nativeSetBackgroundOpaque(long nativeContentViewCoreImpl, boolean opaque);