1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.content.browser;
7 import android.annotation.SuppressLint;
8 import android.app.Activity;
9 import android.app.SearchManager;
10 import android.content.ClipboardManager;
11 import android.content.ContentResolver;
12 import android.content.Context;
13 import android.content.Intent;
14 import android.content.pm.PackageManager;
15 import android.content.res.Configuration;
16 import android.database.ContentObserver;
17 import android.graphics.Bitmap;
18 import android.graphics.Canvas;
19 import android.graphics.Rect;
20 import android.net.Uri;
21 import android.os.Build;
22 import android.os.Bundle;
23 import android.os.Handler;
24 import android.os.ResultReceiver;
25 import android.os.SystemClock;
26 import android.provider.Browser;
27 import android.provider.Settings;
28 import android.text.Editable;
29 import android.text.Selection;
30 import android.text.TextUtils;
31 import android.util.Log;
32 import android.util.Pair;
33 import android.view.ActionMode;
34 import android.view.HapticFeedbackConstants;
35 import android.view.InputDevice;
36 import android.view.KeyEvent;
37 import android.view.MotionEvent;
38 import android.view.View;
39 import android.view.ViewGroup;
40 import android.view.accessibility.AccessibilityEvent;
41 import android.view.accessibility.AccessibilityManager;
42 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
43 import android.view.accessibility.AccessibilityNodeInfo;
44 import android.view.accessibility.AccessibilityNodeProvider;
45 import android.view.inputmethod.EditorInfo;
46 import android.view.inputmethod.InputConnection;
47 import android.view.inputmethod.InputMethodManager;
48 import android.widget.FrameLayout;
50 import com.google.common.annotations.VisibleForTesting;
52 import org.chromium.base.ApiCompatibilityUtils;
53 import org.chromium.base.CalledByNative;
54 import org.chromium.base.CommandLine;
55 import org.chromium.base.JNINamespace;
56 import org.chromium.base.ObserverList;
57 import org.chromium.base.ObserverList.RewindableIterator;
58 import org.chromium.base.TraceEvent;
59 import org.chromium.content.R;
60 import org.chromium.content.browser.ScreenOrientationListener.ScreenOrientationObserver;
61 import org.chromium.content.browser.accessibility.AccessibilityInjector;
62 import org.chromium.content.browser.accessibility.BrowserAccessibilityManager;
63 import org.chromium.content.browser.input.AdapterInputConnection;
64 import org.chromium.content.browser.input.GamepadList;
65 import org.chromium.content.browser.input.ImeAdapter;
66 import org.chromium.content.browser.input.ImeAdapter.AdapterInputConnectionFactory;
67 import org.chromium.content.browser.input.InputMethodManagerWrapper;
68 import org.chromium.content.browser.input.PastePopupMenu;
69 import org.chromium.content.browser.input.PastePopupMenu.PastePopupMenuDelegate;
70 import org.chromium.content.browser.input.PopupTouchHandleDrawable;
71 import org.chromium.content.browser.input.PopupTouchHandleDrawable.PopupTouchHandleDrawableDelegate;
72 import org.chromium.content.browser.input.SelectPopup;
73 import org.chromium.content.browser.input.SelectPopupDialog;
74 import org.chromium.content.browser.input.SelectPopupDropdown;
75 import org.chromium.content.browser.input.SelectPopupItem;
76 import org.chromium.content.browser.input.SelectionEventType;
77 import org.chromium.content.common.ContentSwitches;
78 import org.chromium.content_public.browser.GestureStateListener;
79 import org.chromium.content_public.browser.JavaScriptCallback;
80 import org.chromium.content_public.browser.WebContents;
81 import org.chromium.ui.base.DeviceFormFactor;
82 import org.chromium.ui.base.ViewAndroid;
83 import org.chromium.ui.base.ViewAndroidDelegate;
84 import org.chromium.ui.base.WindowAndroid;
85 import org.chromium.ui.gfx.DeviceDisplayInfo;
87 import java.lang.annotation.Annotation;
88 import java.lang.reflect.Field;
89 import java.util.ArrayList;
90 import java.util.HashMap;
91 import java.util.HashSet;
92 import java.util.List;
96 * Provides a Java-side 'wrapper' around a WebContent (native) instance.
97 * Contains all the major functionality necessary to manage the lifecycle of a ContentView without
98 * being tied to the view system.
100 @JNINamespace("content")
101 public class ContentViewCore
102 implements NavigationClient, AccessibilityStateChangeListener, ScreenOrientationObserver {
104 private static final String TAG = "ContentViewCore";
106 // Used to avoid enabling zooming in / out if resulting zooming will
107 // produce little visible difference.
108 private static final float ZOOM_CONTROLS_EPSILON = 0.007f;
110 // Used to represent gestures for long press and long tap.
111 private static final int IS_LONG_PRESS = 1;
112 private static final int IS_LONG_TAP = 2;
114 // If the embedder adds a JavaScript interface object that contains an indirect reference to
115 // the ContentViewCore, then storing a strong ref to the interface object on the native
116 // side would prevent garbage collection of the ContentViewCore (as that strong ref would
117 // create a new GC root).
118 // For that reason, we store only a weak reference to the interface object on the
119 // native side. However we still need a strong reference on the Java side to
120 // prevent garbage collection if the embedder doesn't maintain their own ref to the
121 // interface object - the Java side ref won't create a new GC root.
122 // This map stores those references. We put into the map on addJavaScriptInterface()
123 // and remove from it in removeJavaScriptInterface(). The annotation class is stored for
124 // the purpose of migrating injected objects from one instance of CVC to another, which
125 // is used by Android WebView to support WebChromeClient.onCreateWindow scenario.
126 private final Map<String, Pair<Object, Class>> mJavaScriptInterfaces =
127 new HashMap<String, Pair<Object, Class>>();
129 // Additionally, we keep track of all Java bound JS objects that are in use on the
130 // current page to ensure that they are not garbage collected until the page is
131 // navigated. This includes interface objects that have been removed
132 // via the removeJavaScriptInterface API and transient objects returned from methods
133 // on the interface object. Note we use HashSet rather than Set as the native side
134 // expects HashSet (no bindings for interfaces).
135 private final HashSet<Object> mRetainedJavaScriptObjects = new HashSet<Object>();
138 * Interface that consumers of {@link ContentViewCore} must implement to allow the proper
139 * dispatching of view methods through the containing view.
142 * All methods with the "super_" prefix should be routed to the parent of the
143 * implementing container view.
145 @SuppressWarnings("javadoc")
146 public interface InternalAccessDelegate {
148 * @see View#drawChild(Canvas, View, long)
150 boolean drawChild(Canvas canvas, View child, long drawingTime);
153 * @see View#onKeyUp(keyCode, KeyEvent)
155 boolean super_onKeyUp(int keyCode, KeyEvent event);
158 * @see View#dispatchKeyEventPreIme(KeyEvent)
160 boolean super_dispatchKeyEventPreIme(KeyEvent event);
163 * @see View#dispatchKeyEvent(KeyEvent)
165 boolean super_dispatchKeyEvent(KeyEvent event);
168 * @see View#onGenericMotionEvent(MotionEvent)
170 boolean super_onGenericMotionEvent(MotionEvent event);
173 * @see View#onConfigurationChanged(Configuration)
175 void super_onConfigurationChanged(Configuration newConfig);
178 * @see View#onScrollChanged(int, int, int, int)
180 void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix);
183 * @see View#awakenScrollBars()
185 boolean awakenScrollBars();
188 * @see View#awakenScrollBars(int, boolean)
190 boolean super_awakenScrollBars(int startDelay, boolean invalidate);
194 * An interface for controlling visibility and state of embedder-provided zoom controls.
196 public interface ZoomControlsDelegate {
198 * Called when it's reasonable to show zoom controls.
200 void invokeZoomPicker();
203 * Called when zoom controls need to be hidden (e.g. when the view hides).
205 void dismissZoomPicker();
208 * Called when page scale has been changed, so the controls can update their state.
210 void updateZoomControls();
214 * An interface that allows the embedder to be notified when the results of
215 * extractSmartClipData are available.
217 public interface SmartClipDataListener {
218 public void onSmartClipDataExtracted(String text, String html, Rect clipRect);
221 private final Context mContext;
222 private ViewGroup mContainerView;
223 private InternalAccessDelegate mContainerViewInternals;
224 private WebContents mWebContents;
225 private WebContentsObserverAndroid mWebContentsObserver;
227 private ContentViewClient mContentViewClient;
229 private ContentSettings mContentSettings;
231 // Native pointer to C++ ContentViewCoreImpl object which will be set by nativeInit().
232 private long mNativeContentViewCore = 0;
234 private final ObserverList<GestureStateListener> mGestureStateListeners;
235 private final RewindableIterator<GestureStateListener> mGestureStateListenersIterator;
236 private ZoomControlsDelegate mZoomControlsDelegate;
238 private PopupZoomer mPopupZoomer;
239 private SelectPopup mSelectPopup;
241 private Runnable mFakeMouseMoveRunnable = null;
243 // Only valid when focused on a text / password field.
244 private ImeAdapter mImeAdapter;
245 private ImeAdapter.AdapterInputConnectionFactory mAdapterInputConnectionFactory;
246 private AdapterInputConnection mInputConnection;
247 private InputMethodManagerWrapper mInputMethodManagerWrapper;
249 // Lazily created paste popup menu, triggered either via long press in an
250 // editable region or from tapping the insertion handle.
251 private PastePopupMenu mPastePopupMenu;
252 private boolean mWasPastePopupShowingOnInsertionDragStart;
254 private PopupTouchHandleDrawableDelegate mTouchHandleDelegate;
256 private PositionObserver mPositionObserver;
258 // Size of the viewport in physical pixels as set from onSizeChanged.
259 private int mViewportWidthPix;
260 private int mViewportHeightPix;
261 private int mPhysicalBackingWidthPix;
262 private int mPhysicalBackingHeightPix;
263 private int mOverdrawBottomHeightPix;
264 private int mViewportSizeOffsetWidthPix;
265 private int mViewportSizeOffsetHeightPix;
267 // Cached copy of all positions and scales as reported by the renderer.
268 private final RenderCoordinates mRenderCoordinates;
270 // Tracks whether a selection is currently active. When applied to selected text, indicates
271 // whether the last selected text is still highlighted.
272 private boolean mHasSelection;
273 private boolean mHasInsertion;
274 private String mLastSelectedText;
275 private boolean mFocusedNodeEditable;
276 private ActionMode mActionMode;
277 private boolean mUnselectAllOnActionModeDismiss;
279 // Delegate that will handle GET downloads, and be notified of completion of POST downloads.
280 private ContentViewDownloadDelegate mDownloadDelegate;
282 // The AccessibilityInjector that handles loading Accessibility scripts into the web page.
283 private AccessibilityInjector mAccessibilityInjector;
285 // Whether native accessibility, i.e. without any script injection, is allowed.
286 private boolean mNativeAccessibilityAllowed;
288 // Whether native accessibility, i.e. without any script injection, has been enabled.
289 private boolean mNativeAccessibilityEnabled;
291 // Handles native accessibility, i.e. without any script injection.
292 private BrowserAccessibilityManager mBrowserAccessibilityManager;
294 // System accessibility service.
295 private final AccessibilityManager mAccessibilityManager;
297 // Accessibility touch exploration state.
298 private boolean mTouchExplorationEnabled;
300 // Whether accessibility focus should be set to the page when it finishes loading.
301 // This only applies if an accessibility service like TalkBack is running.
302 // This is desirable behavior for a browser window, but not for an embedded
304 private boolean mShouldSetAccessibilityFocusOnPageLoad;
306 // Allows us to dynamically respond when the accessibility script injection flag changes.
307 private ContentObserver mAccessibilityScriptInjectionObserver;
309 // Temporary notification to tell onSizeChanged to focus a form element,
310 // because the OSK was just brought up.
311 private final Rect mFocusPreOSKViewportRect = new Rect();
313 // On tap this will store the x, y coordinates of the touch.
314 private int mLastTapX;
315 private int mLastTapY;
317 // Whether a touch scroll sequence is active, used to hide text selection
318 // handles. Note that a scroll sequence will *always* bound a pinch
319 // sequence, so this will also be true for the duration of a pinch gesture.
320 private boolean mTouchScrollInProgress;
322 // The outstanding fling start events that hasn't got fling end yet. It may be > 1 because
323 // onNativeFlingStopped() is called asynchronously.
324 private int mPotentiallyActiveFlingCount;
326 private ViewAndroid mViewAndroid;
328 private SmartClipDataListener mSmartClipDataListener = null;
330 // This holds the state of editable text (e.g. contents of <input>, contenteditable) of
331 // a focused element.
332 // Every time the user, IME, javascript (Blink), autofill etc. modifies the content, the new
333 // state must be reflected to this to keep consistency.
334 private final Editable mEditable;
337 * PID used to indicate an invalid render process.
339 // Keep in sync with the value returned from ContentViewCoreImpl::GetCurrentRendererProcessId()
340 // if there is no render process.
341 public static final int INVALID_RENDER_PROCESS_PID = 0;
343 // Offsets for the events that passes through this ContentViewCore.
344 private float mCurrentTouchOffsetX;
345 private float mCurrentTouchOffsetY;
347 // Offsets for smart clip
348 private int mSmartClipOffsetX;
349 private int mSmartClipOffsetY;
351 // Whether the ContentViewCore requires the WebContents to be fullscreen in order to lock the
352 // screen orientation.
353 private boolean mFullscreenRequiredForOrientationLock = true;
356 * Constructs a new ContentViewCore. Embedders must call initialize() after constructing
357 * a ContentViewCore and before using it.
359 * @param context The context used to create this.
361 public ContentViewCore(Context context) {
364 mAdapterInputConnectionFactory = new AdapterInputConnectionFactory();
365 mInputMethodManagerWrapper = new InputMethodManagerWrapper(mContext);
367 mRenderCoordinates = new RenderCoordinates();
368 float deviceScaleFactor = getContext().getResources().getDisplayMetrics().density;
369 String forceScaleFactor = CommandLine.getInstance().getSwitchValue(
370 ContentSwitches.FORCE_DEVICE_SCALE_FACTOR);
371 if (forceScaleFactor != null) {
372 deviceScaleFactor = Float.valueOf(forceScaleFactor);
374 mRenderCoordinates.setDeviceScaleFactor(deviceScaleFactor);
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 if (view.getParent() == null) {
451 // Ignore. setAnchorViewPosition has been called after the anchor view has
452 // already been released.
455 assert view.getParent() == mContainerViewAtCreation;
457 float scale = (float) DeviceDisplayInfo.create(mContext).getDIPScale();
459 // The anchor view should not go outside the bounds of the ContainerView.
460 int leftMargin = Math.round(x * scale);
461 int topMargin = Math.round(mRenderCoordinates.getContentOffsetYPix() + y * scale);
462 int scaledWidth = Math.round(width * scale);
463 // ContentViewCore currently only supports these two container view types.
464 if (mContainerViewAtCreation instanceof FrameLayout) {
466 if (ApiCompatibilityUtils.isLayoutRtl(mContainerViewAtCreation)) {
467 startMargin = mContainerViewAtCreation.getMeasuredWidth()
468 - Math.round((width + x) * scale);
470 startMargin = leftMargin;
472 if (scaledWidth + startMargin > mContainerViewAtCreation.getWidth()) {
473 scaledWidth = mContainerViewAtCreation.getWidth() - startMargin;
475 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
476 scaledWidth, Math.round(height * scale));
477 ApiCompatibilityUtils.setMarginStart(lp, startMargin);
478 lp.topMargin = topMargin;
479 view.setLayoutParams(lp);
480 } else if (mContainerViewAtCreation instanceof android.widget.AbsoluteLayout) {
481 // This fixes the offset due to a difference in
482 // scrolling model of WebView vs. Chrome.
483 // TODO(sgurun) fix this to use mContainerViewAtCreation.getScroll[X/Y]()
484 // as it naturally accounts for scroll differences between
486 leftMargin += mRenderCoordinates.getScrollXPixInt();
487 topMargin += mRenderCoordinates.getScrollYPixInt();
489 android.widget.AbsoluteLayout.LayoutParams lp =
490 new android.widget.AbsoluteLayout.LayoutParams(
491 scaledWidth, (int) (height * scale), leftMargin, topMargin);
492 view.setLayoutParams(lp);
494 Log.e(TAG, "Unknown layout " + mContainerViewAtCreation.getClass().getName());
499 public void releaseAnchorView(View anchorView) {
500 mContainerViewAtCreation.removeView(anchorView);
506 public void setImeAdapterForTest(ImeAdapter imeAdapter) {
507 mImeAdapter = imeAdapter;
511 public ImeAdapter getImeAdapterForTest() {
516 public void setAdapterInputConnectionFactory(AdapterInputConnectionFactory factory) {
517 mAdapterInputConnectionFactory = factory;
521 public void setInputMethodManagerWrapperForTest(InputMethodManagerWrapper immw) {
522 mInputMethodManagerWrapper = immw;
526 public AdapterInputConnection getInputConnectionForTest() {
527 return mInputConnection;
530 private ImeAdapter createImeAdapter(Context context) {
531 return new ImeAdapter(mInputMethodManagerWrapper,
532 new ImeAdapter.ImeAdapterDelegate() {
534 public void onImeEvent() {
535 if (mPopupZoomer.isShowing()) {
536 mPopupZoomer.hide(true);
538 getContentViewClient().onImeEvent();
543 public void onDismissInput() {
544 getContentViewClient().onImeStateChangeRequested(false);
548 public View getAttachedView() {
549 return mContainerView;
553 public ResultReceiver getNewShowKeyboardReceiver() {
554 return new ResultReceiver(new Handler()) {
556 public void onReceiveResult(int resultCode, Bundle resultData) {
557 getContentViewClient().onImeStateChangeRequested(
558 resultCode == InputMethodManager.RESULT_SHOWN ||
559 resultCode == InputMethodManager.RESULT_UNCHANGED_SHOWN);
560 if (resultCode == InputMethodManager.RESULT_SHOWN) {
561 // If OSK is newly shown, delay the form focus until
562 // the onSizeChanged (in order to adjust relative to the
564 // TODO(jdduke): We should not assume that onSizeChanged will
565 // always be called, crbug.com/294908.
566 getContainerView().getWindowVisibleDisplayFrame(
567 mFocusPreOSKViewportRect);
568 } else if (hasFocus() && resultCode ==
569 InputMethodManager.RESULT_UNCHANGED_SHOWN) {
570 // If the OSK was already there, focus the form immediately.
571 scrollFocusedEditableNodeIntoView();
582 * @param containerView The view that will act as a container for all views created by this.
583 * @param internalDispatcher Handles dispatching all hidden or super methods to the
585 * @param nativeWebContents A pointer to the native web contents.
586 * @param windowAndroid An instance of the WindowAndroid.
588 // Perform important post-construction set up of the ContentViewCore.
589 // We do not require the containing view in the constructor to allow embedders to create a
590 // ContentViewCore without having fully created its containing view. The containing view
591 // is a vital component of the ContentViewCore, so embedders must exercise caution in what
592 // they do with the ContentViewCore before calling initialize().
593 // We supply the nativeWebContents pointer here rather than in the constructor to allow us
594 // to set the private browsing mode at a later point for the WebView implementation.
595 // Note that the caller remains the owner of the nativeWebContents and is responsible for
596 // deleting it after destroying the ContentViewCore.
597 public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
598 long nativeWebContents, WindowAndroid windowAndroid) {
599 setContainerView(containerView);
601 long windowNativePointer = windowAndroid.getNativePointer();
602 assert windowNativePointer != 0;
603 mViewAndroid = new ViewAndroid(windowAndroid, getViewAndroidDelegate());
604 long viewAndroidNativePointer = mViewAndroid.getNativePointer();
605 assert viewAndroidNativePointer != 0;
607 mZoomControlsDelegate = new ZoomControlsDelegate() {
609 public void invokeZoomPicker() {}
611 public void dismissZoomPicker() {}
613 public void updateZoomControls() {}
616 mNativeContentViewCore = nativeInit(
617 nativeWebContents, viewAndroidNativePointer, windowNativePointer,
618 mRetainedJavaScriptObjects);
619 mWebContents = nativeGetWebContentsAndroid(mNativeContentViewCore);
620 mContentSettings = new ContentSettings(this, mNativeContentViewCore);
622 setContainerViewInternals(internalDispatcher);
623 mRenderCoordinates.reset();
624 initPopupZoomer(mContext);
625 mImeAdapter = createImeAdapter(mContext);
627 mAccessibilityInjector = AccessibilityInjector.newInstance(this);
629 mWebContentsObserver = new WebContentsObserverAndroid(mWebContents) {
631 public void didNavigateMainFrame(String url, String baseUrl,
632 boolean isNavigationToDifferentPage, boolean isFragmentNavigation) {
633 if (!isNavigationToDifferentPage) return;
635 resetScrollInProgress();
636 resetGestureDetection();
640 public void renderProcessGone(boolean wasOomProtected) {
642 resetScrollInProgress();
643 // No need to reset gesture detection as the detector will have
644 // been destroyed in the RenderWidgetHostView.
650 * Sets a new container view for this {@link ContentViewCore}.
652 * <p>WARNING: This is not a general purpose method and has been designed with WebView
653 * fullscreen in mind. Please be aware that it might not be appropriate for other use cases
654 * and that it has a number of limitations. For example the PopupZoomer only works with the
655 * container view with which this ContentViewCore has been initialized.
657 * <p>This method only performs a small part of replacing the container view and
658 * embedders are responsible for:
660 * <li>Disconnecting the old container view from this ContentViewCore</li>
661 * <li>Updating the InternalAccessDelegate</li>
662 * <li>Reconciling the state of this ContentViewCore with the new container view</li>
663 * <li>Tearing down and recreating the native GL rendering where appropriate</li>
667 public void setContainerView(ViewGroup containerView) {
669 if (mContainerView != null) {
670 mPastePopupMenu = null;
671 mInputConnection = null;
675 mContainerView = containerView;
676 mPositionObserver = new ViewPositionObserver(mContainerView);
677 String contentDescription = "Web View";
678 if (R.string.accessibility_content_view == 0) {
679 Log.w(TAG, "Setting contentDescription to 'Web View' as no value was specified.");
681 contentDescription = mContext.getResources().getString(
682 R.string.accessibility_content_view);
684 mContainerView.setContentDescription(contentDescription);
685 mContainerView.setWillNotDraw(false);
686 mContainerView.setClickable(true);
691 void onNativeContentViewCoreDestroyed(long nativeContentViewCore) {
692 assert nativeContentViewCore == mNativeContentViewCore;
693 mNativeContentViewCore = 0;
697 * Set the Container view Internals.
698 * @param internalDispatcher Handles dispatching all hidden or super methods to the
701 public void setContainerViewInternals(InternalAccessDelegate internalDispatcher) {
702 mContainerViewInternals = internalDispatcher;
705 private void initPopupZoomer(Context context) {
706 mPopupZoomer = new PopupZoomer(context);
707 mPopupZoomer.setOnVisibilityChangedListener(new PopupZoomer.OnVisibilityChangedListener() {
708 // mContainerView can change, but this OnVisibilityChangedListener can only be used
709 // to add and remove views from the mContainerViewAtCreation.
710 private final ViewGroup mContainerViewAtCreation = mContainerView;
713 public void onPopupZoomerShown(final PopupZoomer zoomer) {
714 mContainerViewAtCreation.post(new Runnable() {
717 if (mContainerViewAtCreation.indexOfChild(zoomer) == -1) {
718 mContainerViewAtCreation.addView(zoomer);
720 assert false : "PopupZoomer should never be shown without being hidden";
727 public void onPopupZoomerHidden(final PopupZoomer zoomer) {
728 mContainerViewAtCreation.post(new Runnable() {
731 if (mContainerViewAtCreation.indexOfChild(zoomer) != -1) {
732 mContainerViewAtCreation.removeView(zoomer);
733 mContainerViewAtCreation.invalidate();
735 assert false : "PopupZoomer should never be hidden without being shown";
741 // TODO(yongsheng): LONG_TAP is not enabled in PopupZoomer. So need to dispatch a LONG_TAP
742 // gesture if a user completes a tap on PopupZoomer UI after a LONG_PRESS gesture.
743 PopupZoomer.OnTapListener listener = new PopupZoomer.OnTapListener() {
744 // mContainerView can change, but this OnTapListener can only be used
745 // with the mContainerViewAtCreation.
746 private final ViewGroup mContainerViewAtCreation = mContainerView;
749 public boolean onSingleTap(View v, MotionEvent e) {
750 mContainerViewAtCreation.requestFocus();
751 if (mNativeContentViewCore != 0) {
752 nativeSingleTap(mNativeContentViewCore, e.getEventTime(), e.getX(), e.getY());
758 public boolean onLongPress(View v, MotionEvent e) {
759 if (mNativeContentViewCore != 0) {
760 nativeLongPress(mNativeContentViewCore, e.getEventTime(), e.getX(), e.getY());
765 mPopupZoomer.setOnTapListener(listener);
769 * Destroy the internal state of the ContentView. This method may only be
770 * called after the ContentView has been removed from the view system. No
771 * other methods may be called on this ContentView after this method has
774 public void destroy() {
775 if (mNativeContentViewCore != 0) {
776 nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore);
779 if (mViewAndroid != null) mViewAndroid.destroy();
780 mNativeContentViewCore = 0;
781 mContentSettings = null;
782 mJavaScriptInterfaces.clear();
783 mRetainedJavaScriptObjects.clear();
784 unregisterAccessibilityContentObserver();
785 mGestureStateListeners.clear();
786 ScreenOrientationListener.getInstance().removeObserver(this);
787 mPositionObserver.clearListener();
790 private void unregisterAccessibilityContentObserver() {
791 if (mAccessibilityScriptInjectionObserver == null) {
794 getContext().getContentResolver().unregisterContentObserver(
795 mAccessibilityScriptInjectionObserver);
796 mAccessibilityScriptInjectionObserver = null;
800 * Returns true initially, false after destroy() has been called.
801 * It is illegal to call any other public method after destroy().
803 public boolean isAlive() {
804 return mNativeContentViewCore != 0;
808 * This is only useful for passing over JNI to native code that requires ContentViewCore*.
809 * @return native ContentViewCore pointer.
812 public long getNativeContentViewCore() {
813 return mNativeContentViewCore;
816 public void setContentViewClient(ContentViewClient client) {
817 if (client == null) {
818 throw new IllegalArgumentException("The client can't be null.");
820 mContentViewClient = client;
824 public ContentViewClient getContentViewClient() {
825 if (mContentViewClient == null) {
826 // We use the Null Object pattern to avoid having to perform a null check in this class.
827 // We create it lazily because most of the time a client will be set almost immediately
828 // after ContentView is created.
829 mContentViewClient = new ContentViewClient();
830 // We don't set the native ContentViewClient pointer here on purpose. The native
831 // implementation doesn't mind a null delegate and using one is better than passing a
832 // Null Object, since we cut down on the number of JNI calls.
834 return mContentViewClient;
837 public int getBackgroundColor() {
838 assert mWebContents != null;
839 return mWebContents.getBackgroundColor();
843 private void onBackgroundColorChanged(int color) {
844 getContentViewClient().onBackgroundColorChanged(color);
848 * Load url without fixing up the url string. Consumers of ContentView are responsible for
849 * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
850 * off during user input).
852 * @param params Parameters for this load.
854 public void loadUrl(LoadUrlParams params) {
855 if (mNativeContentViewCore == 0) return;
857 nativeLoadUrl(mNativeContentViewCore,
860 params.mTransitionType,
861 params.getReferrer() != null ? params.getReferrer().getUrl() : null,
862 params.getReferrer() != null ? params.getReferrer().getPolicy() : 0,
863 params.mUaOverrideOption,
864 params.getExtraHeadersString(),
866 params.mBaseUrlForDataUrl,
867 params.mVirtualUrlForDataUrl,
868 params.mCanLoadLocalResources,
869 params.mIsRendererInitiated);
873 * Stops loading the current web contents.
875 public void stopLoading() {
876 assert mWebContents != null;
881 * Get the URL of the current page.
883 * @return The URL of the current page.
885 public String getUrl() {
886 assert mWebContents != null;
887 return mWebContents.getUrl();
891 * Get the title of the current page.
893 * @return The title of the current page.
895 public String getTitle() {
896 assert mWebContents != null;
897 return mWebContents.getTitle();
901 * Shows an interstitial page driven by the passed in delegate.
903 * @param url The URL being blocked by the interstitial.
904 * @param delegate The delegate handling the interstitial.
907 public void showInterstitialPage(
908 String url, InterstitialPageDelegateAndroid delegate) {
909 assert mWebContents != null;
910 mWebContents.showInterstitialPage(url, delegate.getNative());
914 * @return Whether the page is currently showing an interstitial, such as a bad HTTPS page.
916 public boolean isShowingInterstitialPage() {
917 assert mWebContents != null;
918 return mWebContents.isShowingInterstitialPage();
922 * @return Viewport width in physical pixels as set from onSizeChanged.
925 public int getViewportWidthPix() { return mViewportWidthPix; }
928 * @return Viewport height in physical pixels as set from onSizeChanged.
931 public int getViewportHeightPix() { return mViewportHeightPix; }
934 * @return Width of underlying physical surface.
937 public int getPhysicalBackingWidthPix() { return mPhysicalBackingWidthPix; }
940 * @return Height of underlying physical surface.
943 public int getPhysicalBackingHeightPix() { return mPhysicalBackingHeightPix; }
946 * @return Amount the output surface extends past the bottom of the window viewport.
949 public int getOverdrawBottomHeightPix() { return mOverdrawBottomHeightPix; }
952 * @return The amount to shrink the viewport relative to {@link #getViewportWidthPix()}.
955 public int getViewportSizeOffsetWidthPix() { return mViewportSizeOffsetWidthPix; }
958 * @return The amount to shrink the viewport relative to {@link #getViewportHeightPix()}.
961 public int getViewportSizeOffsetHeightPix() { return mViewportSizeOffsetHeightPix; }
964 * @see android.webkit.WebView#getContentHeight()
966 public float getContentHeightCss() {
967 return mRenderCoordinates.getContentHeightCss();
971 * @see android.webkit.WebView#getContentWidth()
973 public float getContentWidthCss() {
974 return mRenderCoordinates.getContentWidthCss();
977 // TODO(teddchoc): Remove all these navigation controller methods from here and have the
978 // embedders manage it.
980 * @return Whether the current WebContents has a previous navigation entry.
982 public boolean canGoBack() {
983 return mWebContents != null && mWebContents.getNavigationController().canGoBack();
987 * @return Whether the current WebContents has a navigation entry after the current one.
989 public boolean canGoForward() {
990 return mWebContents != null && mWebContents.getNavigationController().canGoForward();
994 * @param offset The offset into the navigation history.
995 * @return Whether we can move in history by given offset
997 public boolean canGoToOffset(int offset) {
998 return mWebContents != null &&
999 mWebContents.getNavigationController().canGoToOffset(offset);
1003 * Navigates to the specified offset from the "current entry". Does nothing if the offset is out
1005 * @param offset The offset into the navigation history.
1007 public void goToOffset(int offset) {
1008 if (mWebContents != null) mWebContents.getNavigationController().goToOffset(offset);
1012 public void goToNavigationIndex(int index) {
1013 if (mWebContents != null) {
1014 mWebContents.getNavigationController().goToNavigationIndex(index);
1019 * Goes to the navigation entry before the current one.
1021 public void goBack() {
1022 if (mWebContents != null) mWebContents.getNavigationController().goBack();
1026 * Goes to the navigation entry following the current one.
1028 public void goForward() {
1029 if (mWebContents != null) mWebContents.getNavigationController().goForward();
1033 * Loads the current navigation if there is a pending lazy load (after tab restore).
1035 public void loadIfNecessary() {
1036 if (mWebContents != null) mWebContents.getNavigationController().loadIfNecessary();
1040 * Requests the current navigation to be loaded upon the next call to loadIfNecessary().
1042 public void requestRestoreLoad() {
1043 if (mWebContents != null) mWebContents.getNavigationController().requestRestoreLoad();
1047 * Reload the current page.
1049 public void reload(boolean checkForRepost) {
1050 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1051 if (mWebContents != null) mWebContents.getNavigationController().reload(checkForRepost);
1055 * Reload the current page, ignoring the contents of the cache.
1057 public void reloadIgnoringCache(boolean checkForRepost) {
1058 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1059 if (mWebContents != null) mWebContents.getNavigationController().reloadIgnoringCache(
1064 * Cancel the pending reload.
1066 public void cancelPendingReload() {
1067 if (mWebContents != null) mWebContents.getNavigationController().cancelPendingReload();
1071 * Continue the pending reload.
1073 public void continuePendingReload() {
1074 if (mWebContents != null) mWebContents.getNavigationController().continuePendingReload();
1078 * Clears the ContentViewCore's page history in both the backwards and
1079 * forwards directions.
1081 public void clearHistory() {
1082 if (mNativeContentViewCore != 0) nativeClearHistory(mNativeContentViewCore);
1086 * @return The selected text (empty if no text selected).
1088 public String getSelectedText() {
1089 return mHasSelection ? mLastSelectedText : "";
1093 * @return Whether the current selection is editable (false if no text selected).
1095 public boolean isSelectionEditable() {
1096 return mHasSelection ? mFocusedNodeEditable : false;
1100 * @return Whether the current focused node is editable.
1102 public boolean isFocusedNodeEditable() {
1103 return mFocusedNodeEditable;
1106 // End FrameLayout overrides.
1109 * @see View#onTouchEvent(MotionEvent)
1111 public boolean onTouchEvent(MotionEvent event) {
1112 final boolean isTouchHandleEvent = false;
1113 return onTouchEventImpl(event, isTouchHandleEvent);
1116 private boolean onTouchEventImpl(MotionEvent event, boolean isTouchHandleEvent) {
1117 TraceEvent.begin("onTouchEvent");
1119 int eventAction = event.getActionMasked();
1121 if (eventAction == MotionEvent.ACTION_DOWN) {
1122 cancelRequestToScrollFocusedEditableNodeIntoView();
1125 if (SPenSupport.isSPenSupported(mContext))
1126 eventAction = SPenSupport.convertSPenEventAction(eventAction);
1127 if (!isValidTouchEventActionForNative(eventAction)) return false;
1129 if (mNativeContentViewCore == 0) return false;
1131 // A zero offset is quite common, in which case the unnecessary copy should be avoided.
1132 MotionEvent offset = null;
1133 if (mCurrentTouchOffsetX != 0 || mCurrentTouchOffsetY != 0) {
1134 offset = createOffsetMotionEvent(event);
1138 final int pointerCount = event.getPointerCount();
1139 final boolean consumed = nativeOnTouchEvent(mNativeContentViewCore, event,
1140 event.getEventTime(), eventAction,
1141 pointerCount, event.getHistorySize(), event.getActionIndex(),
1142 event.getX(), event.getY(),
1143 pointerCount > 1 ? event.getX(1) : 0,
1144 pointerCount > 1 ? event.getY(1) : 0,
1145 event.getPointerId(0), pointerCount > 1 ? event.getPointerId(1) : -1,
1146 event.getTouchMajor(), pointerCount > 1 ? event.getTouchMajor(1) : 0,
1147 event.getRawX(), event.getRawY(),
1148 event.getToolType(0),
1149 pointerCount > 1 ? event.getToolType(1) : MotionEvent.TOOL_TYPE_UNKNOWN,
1150 event.getButtonState(),
1151 isTouchHandleEvent);
1153 if (offset != null) offset.recycle();
1156 TraceEvent.end("onTouchEvent");
1160 private static boolean isValidTouchEventActionForNative(int eventAction) {
1161 // Only these actions have any effect on gesture detection. Other
1162 // actions have no corresponding WebTouchEvent type and may confuse the
1163 // touch pipline, so we ignore them entirely.
1164 return eventAction == MotionEvent.ACTION_DOWN
1165 || eventAction == MotionEvent.ACTION_UP
1166 || eventAction == MotionEvent.ACTION_CANCEL
1167 || eventAction == MotionEvent.ACTION_MOVE
1168 || eventAction == MotionEvent.ACTION_POINTER_DOWN
1169 || eventAction == MotionEvent.ACTION_POINTER_UP;
1172 public void setIgnoreRemainingTouchEvents() {
1173 resetGestureDetection();
1176 public boolean isScrollInProgress() {
1177 return mTouchScrollInProgress || mPotentiallyActiveFlingCount > 0;
1180 @SuppressWarnings("unused")
1182 private void onFlingStartEventConsumed(int vx, int vy) {
1183 mTouchScrollInProgress = false;
1184 mPotentiallyActiveFlingCount++;
1185 for (mGestureStateListenersIterator.rewind();
1186 mGestureStateListenersIterator.hasNext();) {
1187 mGestureStateListenersIterator.next().onFlingStartGesture(
1188 vx, vy, computeVerticalScrollOffset(), computeVerticalScrollExtent());
1192 @SuppressWarnings("unused")
1194 private void onFlingStartEventHadNoConsumer(int vx, int vy) {
1195 mTouchScrollInProgress = false;
1196 for (mGestureStateListenersIterator.rewind();
1197 mGestureStateListenersIterator.hasNext();) {
1198 mGestureStateListenersIterator.next().onUnhandledFlingStartEvent(vx, vy);
1202 @SuppressWarnings("unused")
1204 private void onFlingCancelEventAck() {
1205 updateGestureStateListener(GestureEventType.FLING_CANCEL);
1208 @SuppressWarnings("unused")
1210 private void onScrollBeginEventAck() {
1211 mTouchScrollInProgress = true;
1213 mZoomControlsDelegate.invokeZoomPicker();
1214 updateGestureStateListener(GestureEventType.SCROLL_START);
1217 @SuppressWarnings("unused")
1219 private void onScrollUpdateGestureConsumed() {
1220 mZoomControlsDelegate.invokeZoomPicker();
1221 for (mGestureStateListenersIterator.rewind();
1222 mGestureStateListenersIterator.hasNext();) {
1223 mGestureStateListenersIterator.next().onScrollUpdateGestureConsumed();
1227 @SuppressWarnings("unused")
1229 private void onScrollEndEventAck() {
1230 if (!mTouchScrollInProgress) return;
1231 mTouchScrollInProgress = false;
1232 updateGestureStateListener(GestureEventType.SCROLL_END);
1235 @SuppressWarnings("unused")
1237 private void onPinchBeginEventAck() {
1238 updateGestureStateListener(GestureEventType.PINCH_BEGIN);
1241 @SuppressWarnings("unused")
1243 private void onPinchEndEventAck() {
1244 updateGestureStateListener(GestureEventType.PINCH_END);
1247 @SuppressWarnings("unused")
1249 private void onSingleTapEventAck(boolean consumed, int x, int y) {
1250 for (mGestureStateListenersIterator.rewind();
1251 mGestureStateListenersIterator.hasNext();) {
1252 mGestureStateListenersIterator.next().onSingleTap(consumed, x, y);
1257 * Called just prior to a tap or press gesture being forwarded to the renderer.
1259 @SuppressWarnings("unused")
1261 private boolean filterTapOrPressEvent(int type, int x, int y) {
1262 if (type == GestureEventType.LONG_PRESS && offerLongPressToEmbedder()) {
1265 updateForTapOrPress(type, x, y);
1270 public void sendDoubleTapForTest(long timeMs, int x, int y) {
1271 if (mNativeContentViewCore == 0) return;
1272 nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1276 public void flingForTest(long timeMs, int x, int y, int velocityX, int velocityY) {
1277 if (mNativeContentViewCore == 0) return;
1278 nativeFlingCancel(mNativeContentViewCore, timeMs);
1279 nativeScrollBegin(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1280 nativeFlingStart(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1284 * Cancel any fling gestures active.
1285 * @param timeMs Current time (in milliseconds).
1287 public void cancelFling(long timeMs) {
1288 if (mNativeContentViewCore == 0) return;
1289 nativeFlingCancel(mNativeContentViewCore, timeMs);
1293 * Add a listener that gets alerted on gesture state changes.
1294 * @param listener Listener to add.
1296 public void addGestureStateListener(GestureStateListener listener) {
1297 mGestureStateListeners.addObserver(listener);
1301 * Removes a listener that was added to watch for gesture state changes.
1302 * @param listener Listener to remove.
1304 public void removeGestureStateListener(GestureStateListener listener) {
1305 mGestureStateListeners.removeObserver(listener);
1308 void updateGestureStateListener(int gestureType) {
1309 for (mGestureStateListenersIterator.rewind();
1310 mGestureStateListenersIterator.hasNext();) {
1311 GestureStateListener listener = mGestureStateListenersIterator.next();
1312 switch (gestureType) {
1313 case GestureEventType.PINCH_BEGIN:
1314 listener.onPinchStarted();
1316 case GestureEventType.PINCH_END:
1317 listener.onPinchEnded();
1319 case GestureEventType.FLING_END:
1320 listener.onFlingEndGesture(
1321 computeVerticalScrollOffset(),
1322 computeVerticalScrollExtent());
1324 case GestureEventType.FLING_CANCEL:
1325 listener.onFlingCancelGesture();
1327 case GestureEventType.SCROLL_START:
1328 listener.onScrollStarted(
1329 computeVerticalScrollOffset(),
1330 computeVerticalScrollExtent());
1332 case GestureEventType.SCROLL_END:
1333 listener.onScrollEnded(
1334 computeVerticalScrollOffset(),
1335 computeVerticalScrollExtent());
1344 * Inserts the provided markup sandboxed into the frame.
1346 public void setupTransitionView(String markup) {
1347 assert mWebContents != null;
1348 mWebContents.setupTransitionView(markup);
1352 * Hides transition elements specified by the selector, and activates any
1353 * exiting-transition stylesheets.
1355 public void beginExitTransition(String cssSelector) {
1356 assert mWebContents != null;
1357 mWebContents.beginExitTransition(cssSelector);
1361 * Requests the renderer insert a link to the specified stylesheet in the
1362 * main frame's document.
1364 public void addStyleSheetByURL(String url) {
1365 assert mWebContents != null;
1366 mWebContents.addStyleSheetByURL(url);
1370 * Injects the passed Javascript code in the current page and evaluates it.
1371 * If a result is required, pass in a callback.
1372 * Used in automation tests.
1374 * @param script The Javascript to execute.
1375 * @param callback The callback to be fired off when a result is ready. The script's
1376 * result will be json encoded and passed as the parameter, and the call
1377 * will be made on the main thread.
1378 * If no result is required, pass null.
1380 public void evaluateJavaScript(String script, JavaScriptCallback callback) {
1381 assert mWebContents != null;
1382 mWebContents.evaluateJavaScript(script, callback, false);
1386 * Injects the passed Javascript code in the current page and evaluates it.
1387 * If there is no page existing, a new one will be created.
1389 * @param script The Javascript to execute.
1391 public void evaluateJavaScriptEvenIfNotYetNavigated(String script) {
1392 assert mWebContents != null;
1393 mWebContents.evaluateJavaScript(script, null, true);
1397 * Post a message to a frame.
1398 * TODO(sgurun) also add support for transferring a message channel port.
1400 * @param frameName The name of the frame. If the name is null the message is posted
1401 * to the main frame.
1402 * @param message The message
1403 * @param sourceOrigin The source origin
1404 * @param targetOrigin The target origin
1406 public void postMessageToFrame(String frameName, String message,
1407 String sourceOrigin, String targetOrigin) {
1408 if (mNativeContentViewCore == 0) return;
1409 nativePostMessageToFrame(mNativeContentViewCore, frameName, message, sourceOrigin,
1414 * To be called when the ContentView is shown.
1416 public void onShow() {
1417 assert mWebContents != null;
1418 mWebContents.onShow();
1419 setAccessibilityState(mAccessibilityManager.isEnabled());
1423 * @return The ID of the renderer process that backs this tab or
1424 * {@link #INVALID_RENDER_PROCESS_PID} if there is none.
1426 public int getCurrentRenderProcessId() {
1427 return nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1431 * To be called when the ContentView is hidden.
1433 public void onHide() {
1434 assert mWebContents != null;
1436 setInjectedAccessibility(false);
1437 mWebContents.onHide();
1441 * Return the ContentSettings object used to retrieve the settings for this
1442 * ContentViewCore. For modifications, ChromeNativePreferences is to be used.
1443 * @return A ContentSettings object that can be used to retrieve this
1444 * ContentViewCore's settings.
1446 public ContentSettings getContentSettings() {
1447 return mContentSettings;
1450 private void hidePopups() {
1451 mUnselectAllOnActionModeDismiss = true;
1452 hideSelectActionBar();
1458 public void hideSelectActionBar() {
1459 if (mActionMode != null) {
1460 mActionMode.finish();
1465 public boolean isSelectActionBarShowing() {
1466 return mActionMode != null;
1469 private void resetGestureDetection() {
1470 if (mNativeContentViewCore == 0) return;
1471 nativeResetGestureDetection(mNativeContentViewCore);
1475 * @see View#onAttachedToWindow()
1477 @SuppressWarnings("javadoc")
1478 public void onAttachedToWindow() {
1479 setAccessibilityState(mAccessibilityManager.isEnabled());
1481 ScreenOrientationListener.getInstance().addObserver(this, mContext);
1482 GamepadList.onAttachedToWindow(mContext);
1486 * @see View#onDetachedFromWindow()
1488 @SuppressWarnings("javadoc")
1489 @SuppressLint("MissingSuperCall")
1490 public void onDetachedFromWindow() {
1491 setInjectedAccessibility(false);
1493 mZoomControlsDelegate.dismissZoomPicker();
1494 unregisterAccessibilityContentObserver();
1496 ScreenOrientationListener.getInstance().removeObserver(this);
1497 GamepadList.onDetachedFromWindow();
1501 * @see View#onVisibilityChanged(android.view.View, int)
1503 public void onVisibilityChanged(View changedView, int visibility) {
1504 if (visibility != View.VISIBLE) {
1505 mZoomControlsDelegate.dismissZoomPicker();
1510 * @see View#onCreateInputConnection(EditorInfo)
1512 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1513 if (!mImeAdapter.hasTextInputType()) {
1514 // Although onCheckIsTextEditor will return false in this case, the EditorInfo
1515 // is still used by the InputMethodService. Need to make sure the IME doesn't
1516 // enter fullscreen mode.
1517 outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
1519 mInputConnection = mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter,
1520 mEditable, outAttrs);
1521 return mInputConnection;
1525 public AdapterInputConnection getAdapterInputConnectionForTest() {
1526 return mInputConnection;
1530 public Editable getEditableForTest() {
1535 * @see View#onCheckIsTextEditor()
1537 public boolean onCheckIsTextEditor() {
1538 return mImeAdapter.hasTextInputType();
1542 * @see View#onConfigurationChanged(Configuration)
1544 @SuppressWarnings("javadoc")
1545 public void onConfigurationChanged(Configuration newConfig) {
1548 if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
1549 if (mNativeContentViewCore != 0) {
1550 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
1551 ImeAdapter.getTextInputTypeNone(), 0 /* no flags */);
1553 mInputMethodManagerWrapper.restartInput(mContainerView);
1555 mContainerViewInternals.super_onConfigurationChanged(newConfig);
1557 // To request layout has side effect, but it seems OK as it only happen in
1558 // onConfigurationChange and layout has to be changed in most case.
1559 mContainerView.requestLayout();
1564 * @see View#onSizeChanged(int, int, int, int)
1566 @SuppressWarnings("javadoc")
1567 public void onSizeChanged(int wPix, int hPix, int owPix, int ohPix) {
1568 if (getViewportWidthPix() == wPix && getViewportHeightPix() == hPix) return;
1570 mViewportWidthPix = wPix;
1571 mViewportHeightPix = hPix;
1572 if (mNativeContentViewCore != 0) {
1573 nativeWasResized(mNativeContentViewCore);
1576 updateAfterSizeChanged();
1580 * Called when the underlying surface the compositor draws to changes size.
1581 * This may be larger than the viewport size.
1583 public void onPhysicalBackingSizeChanged(int wPix, int hPix) {
1584 if (mPhysicalBackingWidthPix == wPix && mPhysicalBackingHeightPix == hPix) return;
1586 mPhysicalBackingWidthPix = wPix;
1587 mPhysicalBackingHeightPix = hPix;
1589 if (mNativeContentViewCore != 0) {
1590 nativeWasResized(mNativeContentViewCore);
1595 * Called when the amount the surface is overdrawing off the bottom has changed.
1596 * @param overdrawHeightPix The overdraw height.
1598 public void onOverdrawBottomHeightChanged(int overdrawHeightPix) {
1599 if (mOverdrawBottomHeightPix == overdrawHeightPix) return;
1601 mOverdrawBottomHeightPix = overdrawHeightPix;
1603 if (mNativeContentViewCore != 0) {
1604 nativeWasResized(mNativeContentViewCore);
1608 private void updateAfterSizeChanged() {
1609 mPopupZoomer.hide(false);
1611 // Execute a delayed form focus operation because the OSK was brought
1613 if (!mFocusPreOSKViewportRect.isEmpty()) {
1614 Rect rect = new Rect();
1615 getContainerView().getWindowVisibleDisplayFrame(rect);
1616 if (!rect.equals(mFocusPreOSKViewportRect)) {
1617 // Only assume the OSK triggered the onSizeChanged if width was preserved.
1618 if (rect.width() == mFocusPreOSKViewportRect.width()) {
1619 scrollFocusedEditableNodeIntoView();
1621 cancelRequestToScrollFocusedEditableNodeIntoView();
1626 private void cancelRequestToScrollFocusedEditableNodeIntoView() {
1627 // Zero-ing the rect will prevent |updateAfterSizeChanged()| from
1628 // issuing the delayed form focus event.
1629 mFocusPreOSKViewportRect.setEmpty();
1632 private void scrollFocusedEditableNodeIntoView() {
1633 assert mWebContents != null;
1634 mWebContents.scrollFocusedEditableNodeIntoView();
1638 * Selects the word around the caret, if any.
1639 * The caller can check if selection actually occurred by listening to OnSelectionChanged.
1641 public void selectWordAroundCaret() {
1642 assert mWebContents != null;
1643 mWebContents.selectWordAroundCaret();
1647 * @see View#onWindowFocusChanged(boolean)
1649 public void onWindowFocusChanged(boolean hasWindowFocus) {
1650 if (!hasWindowFocus) resetGestureDetection();
1653 public void onFocusChanged(boolean gainFocus) {
1656 cancelRequestToScrollFocusedEditableNodeIntoView();
1660 if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1664 * @see View#onKeyUp(int, KeyEvent)
1666 public boolean onKeyUp(int keyCode, KeyEvent event) {
1667 if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1668 mPopupZoomer.hide(true);
1671 return mContainerViewInternals.super_onKeyUp(keyCode, event);
1675 * @see View#dispatchKeyEventPreIme(KeyEvent)
1677 public boolean dispatchKeyEventPreIme(KeyEvent event) {
1680 return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1687 * @see View#dispatchKeyEvent(KeyEvent)
1689 public boolean dispatchKeyEvent(KeyEvent event) {
1690 if (GamepadList.dispatchKeyEvent(event)) return true;
1691 if (getContentViewClient().shouldOverrideKeyEvent(event)) {
1692 return mContainerViewInternals.super_dispatchKeyEvent(event);
1695 if (mImeAdapter.dispatchKeyEvent(event)) return true;
1697 return mContainerViewInternals.super_dispatchKeyEvent(event);
1701 * @see View#onHoverEvent(MotionEvent)
1702 * Mouse move events are sent on hover enter, hover move and hover exit.
1703 * They are sent on hover exit because sometimes it acts as both a hover
1704 * move and hover exit.
1706 public boolean onHoverEvent(MotionEvent event) {
1707 TraceEvent.begin("onHoverEvent");
1708 MotionEvent offset = createOffsetMotionEvent(event);
1710 if (mBrowserAccessibilityManager != null) {
1711 return mBrowserAccessibilityManager.onHoverEvent(offset);
1714 // Work around Android bug where the x, y coordinates of a hover exit
1715 // event are incorrect when touch exploration is on.
1716 if (mTouchExplorationEnabled && offset.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
1720 mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1721 if (mNativeContentViewCore != 0) {
1722 nativeSendMouseMoveEvent(mNativeContentViewCore, offset.getEventTime(),
1723 offset.getX(), offset.getY());
1728 TraceEvent.end("onHoverEvent");
1733 * @see View#onGenericMotionEvent(MotionEvent)
1735 public boolean onGenericMotionEvent(MotionEvent event) {
1736 if (GamepadList.onGenericMotionEvent(event)) return true;
1737 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1738 switch (event.getAction()) {
1739 case MotionEvent.ACTION_SCROLL:
1740 if (mNativeContentViewCore == 0) return false;
1742 nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
1743 event.getX(), event.getY(),
1744 event.getAxisValue(MotionEvent.AXIS_VSCROLL));
1746 mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1747 // Send a delayed onMouseMove event so that we end
1748 // up hovering over the right position after the scroll.
1749 final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event);
1750 mFakeMouseMoveRunnable = new Runnable() {
1753 onHoverEvent(eventFakeMouseMove);
1754 eventFakeMouseMove.recycle();
1757 mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1761 return mContainerViewInternals.super_onGenericMotionEvent(event);
1765 * Sets the current amount to offset incoming touch events by. This is used to handle content
1766 * moving and not lining up properly with the android input system.
1767 * @param dx The X offset in pixels to shift touch events.
1768 * @param dy The Y offset in pixels to shift touch events.
1770 public void setCurrentMotionEventOffsets(float dx, float dy) {
1771 mCurrentTouchOffsetX = dx;
1772 mCurrentTouchOffsetY = dy;
1775 private MotionEvent createOffsetMotionEvent(MotionEvent src) {
1776 MotionEvent dst = MotionEvent.obtain(src);
1777 dst.offsetLocation(mCurrentTouchOffsetX, mCurrentTouchOffsetY);
1782 * @see View#scrollBy(int, int)
1783 * Currently the ContentView scrolling happens in the native side. In
1784 * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
1785 * are overridden, so that View's mScrollX and mScrollY will be unchanged at
1786 * (0, 0). This is critical for drawing ContentView correctly.
1788 public void scrollBy(int xPix, int yPix) {
1789 if (mNativeContentViewCore != 0) {
1790 nativeScrollBy(mNativeContentViewCore,
1791 SystemClock.uptimeMillis(), 0, 0, xPix, yPix);
1796 * @see View#scrollTo(int, int)
1798 public void scrollTo(int xPix, int yPix) {
1799 if (mNativeContentViewCore == 0) return;
1800 final float xCurrentPix = mRenderCoordinates.getScrollXPix();
1801 final float yCurrentPix = mRenderCoordinates.getScrollYPix();
1802 final float dxPix = xPix - xCurrentPix;
1803 final float dyPix = yPix - yCurrentPix;
1804 if (dxPix != 0 || dyPix != 0) {
1805 long time = SystemClock.uptimeMillis();
1806 nativeScrollBegin(mNativeContentViewCore, time,
1807 xCurrentPix, yCurrentPix, -dxPix, -dyPix);
1808 nativeScrollBy(mNativeContentViewCore,
1809 time, xCurrentPix, yCurrentPix, dxPix, dyPix);
1810 nativeScrollEnd(mNativeContentViewCore, time);
1814 // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1816 public int getNativeScrollXForTest() {
1817 return mRenderCoordinates.getScrollXPixInt();
1820 // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1822 public int getNativeScrollYForTest() {
1823 return mRenderCoordinates.getScrollYPixInt();
1827 * @see View#computeHorizontalScrollExtent()
1829 @SuppressWarnings("javadoc")
1830 public int computeHorizontalScrollExtent() {
1831 return mRenderCoordinates.getLastFrameViewportWidthPixInt();
1835 * @see View#computeHorizontalScrollOffset()
1837 @SuppressWarnings("javadoc")
1838 public int computeHorizontalScrollOffset() {
1839 return mRenderCoordinates.getScrollXPixInt();
1843 * @see View#computeHorizontalScrollRange()
1845 @SuppressWarnings("javadoc")
1846 public int computeHorizontalScrollRange() {
1847 return mRenderCoordinates.getContentWidthPixInt();
1851 * @see View#computeVerticalScrollExtent()
1853 @SuppressWarnings("javadoc")
1854 public int computeVerticalScrollExtent() {
1855 return mRenderCoordinates.getLastFrameViewportHeightPixInt();
1859 * @see View#computeVerticalScrollOffset()
1861 @SuppressWarnings("javadoc")
1862 public int computeVerticalScrollOffset() {
1863 return mRenderCoordinates.getScrollYPixInt();
1867 * @see View#computeVerticalScrollRange()
1869 @SuppressWarnings("javadoc")
1870 public int computeVerticalScrollRange() {
1871 return mRenderCoordinates.getContentHeightPixInt();
1874 // End FrameLayout overrides.
1877 * @see View#awakenScrollBars(int, boolean)
1879 @SuppressWarnings("javadoc")
1880 public boolean awakenScrollBars(int startDelay, boolean invalidate) {
1881 // For the default implementation of ContentView which draws the scrollBars on the native
1882 // side, calling this function may get us into a bad state where we keep drawing the
1883 // scrollBars, so disable it by always returning false.
1884 if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
1887 return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
1891 private void updateForTapOrPress(int type, float xPix, float yPix) {
1892 if (type != GestureEventType.SINGLE_TAP_CONFIRMED
1893 && type != GestureEventType.SINGLE_TAP_UP
1894 && type != GestureEventType.LONG_PRESS
1895 && type != GestureEventType.LONG_TAP) {
1899 if (mContainerView.isFocusable() && mContainerView.isFocusableInTouchMode()
1900 && !mContainerView.isFocused()) {
1901 mContainerView.requestFocus();
1904 if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
1906 mLastTapX = (int) xPix;
1907 mLastTapY = (int) yPix;
1911 * @return The x coordinate for the last point that a tap or press gesture was initiated from.
1913 public int getLastTapX() {
1918 * @return The y coordinate for the last point that a tap or press gesture was initiated from.
1920 public int getLastTapY() {
1924 public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
1925 mZoomControlsDelegate = zoomControlsDelegate;
1928 public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
1929 if (mNativeContentViewCore == 0) return;
1930 nativeSetMultiTouchZoomSupportEnabled(mNativeContentViewCore, supportsMultiTouchZoom);
1933 public void updateDoubleTapSupport(boolean supportsDoubleTap) {
1934 if (mNativeContentViewCore == 0) return;
1935 nativeSetDoubleTapSupportEnabled(mNativeContentViewCore, supportsDoubleTap);
1938 public void selectPopupMenuItems(int[] indices) {
1939 if (mNativeContentViewCore != 0) {
1940 nativeSelectPopupMenuItems(mNativeContentViewCore, indices);
1942 mSelectPopup = null;
1946 * Send the screen orientation value to the renderer.
1949 void sendOrientationChangeEvent(int orientation) {
1950 if (mNativeContentViewCore == 0) return;
1952 nativeSendOrientationChangeEvent(mNativeContentViewCore, orientation);
1956 * Register the delegate to be used when content can not be handled by
1957 * the rendering engine, and should be downloaded instead. This will replace
1958 * the current delegate, if any.
1959 * @param delegate An implementation of ContentViewDownloadDelegate.
1961 public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
1962 mDownloadDelegate = delegate;
1965 // Called by DownloadController.
1966 ContentViewDownloadDelegate getDownloadDelegate() {
1967 return mDownloadDelegate;
1970 private void showSelectActionBar() {
1971 if (mActionMode != null) {
1972 mActionMode.invalidate();
1976 // Start a new action mode with a SelectActionModeCallback.
1977 SelectActionModeCallback.ActionHandler actionHandler =
1978 new SelectActionModeCallback.ActionHandler() {
1980 public void selectAll() {
1981 mImeAdapter.selectAll();
1990 public void copy() {
1995 public void paste() {
1996 mImeAdapter.paste();
2000 public void share() {
2001 final String query = getSelectedText();
2002 if (TextUtils.isEmpty(query)) return;
2004 Intent send = new Intent(Intent.ACTION_SEND);
2005 send.setType("text/plain");
2006 send.putExtra(Intent.EXTRA_TEXT, query);
2008 Intent i = Intent.createChooser(send, getContext().getString(
2009 R.string.actionbar_share));
2010 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2011 getContext().startActivity(i);
2012 } catch (android.content.ActivityNotFoundException ex) {
2013 // If no app handles it, do nothing.
2018 public void search() {
2019 final String query = getSelectedText();
2020 if (TextUtils.isEmpty(query)) return;
2022 // See if ContentViewClient wants to override
2023 if (getContentViewClient().doesPerformWebSearch()) {
2024 getContentViewClient().performWebSearch(query);
2028 Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
2029 i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2030 i.putExtra(SearchManager.QUERY, query);
2031 i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
2032 if (!(getContext() instanceof Activity)) {
2033 i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2036 getContext().startActivity(i);
2037 } catch (android.content.ActivityNotFoundException ex) {
2038 // If no app handles it, do nothing.
2043 public boolean isSelectionPassword() {
2044 return mImeAdapter.isSelectionPassword();
2048 public boolean isSelectionEditable() {
2049 return mFocusedNodeEditable;
2053 public void onDestroyActionMode() {
2055 if (mUnselectAllOnActionModeDismiss) {
2057 if (isSelectionEditable()) {
2058 int selectionEnd = Selection.getSelectionEnd(mEditable);
2059 mInputConnection.setSelection(selectionEnd, selectionEnd);
2061 mImeAdapter.unselect();
2064 getContentViewClient().onContextualActionBarHidden();
2068 public boolean isShareAvailable() {
2069 Intent intent = new Intent(Intent.ACTION_SEND);
2070 intent.setType("text/plain");
2071 return getContext().getPackageManager().queryIntentActivities(intent,
2072 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2076 public boolean isWebSearchAvailable() {
2077 if (getContentViewClient().doesPerformWebSearch()) return true;
2078 Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
2079 intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2080 return getContext().getPackageManager().queryIntentActivities(intent,
2081 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2085 // On ICS, startActionMode throws an NPE when getParent() is null.
2086 if (mContainerView.getParent() != null) {
2087 assert mWebContents != null;
2088 mActionMode = mContainerView.startActionMode(
2089 getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler,
2090 mWebContents.isIncognito()));
2092 mUnselectAllOnActionModeDismiss = true;
2093 if (mActionMode == null) {
2094 // There is no ActionMode, so remove the selection.
2095 mImeAdapter.unselect();
2097 getContentViewClient().onContextualActionBarShown();
2102 * Clears the current text selection.
2104 public void clearSelection() {
2105 mImeAdapter.unselect();
2108 private void hidePastePopup() {
2109 if (mPastePopupMenu == null) return;
2110 mPastePopupMenu.hide();
2114 private void onSelectionEvent(int eventType, float posXDip, float posYDip) {
2115 switch (eventType) {
2116 case SelectionEventType.SELECTION_SHOWN:
2117 mHasSelection = true;
2118 // TODO(cjhopman): Remove this when there is a better signal that long press caused
2119 // a selection. See http://crbug.com/150151.
2120 mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2121 showSelectActionBar();
2124 case SelectionEventType.SELECTION_CLEARED:
2125 mHasSelection = false;
2126 mUnselectAllOnActionModeDismiss = false;
2127 hideSelectActionBar();
2130 case SelectionEventType.SELECTION_DRAG_STARTED:
2133 case SelectionEventType.SELECTION_DRAG_STOPPED:
2136 case SelectionEventType.INSERTION_SHOWN:
2137 mHasInsertion = true;
2140 case SelectionEventType.INSERTION_MOVED:
2141 if (mPastePopupMenu == null) break;
2142 if (!isScrollInProgress() && mPastePopupMenu.isShowing()) {
2143 showPastePopup((int) posXDip, (int) posYDip);
2149 case SelectionEventType.INSERTION_TAPPED:
2150 if (mWasPastePopupShowingOnInsertionDragStart)
2153 showPastePopup((int) posXDip, (int) posYDip);
2156 case SelectionEventType.INSERTION_CLEARED:
2157 mHasInsertion = false;
2161 case SelectionEventType.INSERTION_DRAG_STARTED:
2162 mWasPastePopupShowingOnInsertionDragStart =
2163 mPastePopupMenu != null && mPastePopupMenu.isShowing();
2168 assert false : "Invalid selection event type.";
2170 getContentViewClient().onSelectionEvent(eventType);
2173 public boolean getUseDesktopUserAgent() {
2174 if (mNativeContentViewCore != 0) {
2175 return nativeGetUseDesktopUserAgent(mNativeContentViewCore);
2181 * Set whether or not we're using a desktop user agent for the currently loaded page.
2182 * @param override If true, use a desktop user agent. Use a mobile one otherwise.
2183 * @param reloadOnChange Reload the page if the UA has changed.
2185 public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange) {
2186 if (mNativeContentViewCore != 0) {
2187 nativeSetUseDesktopUserAgent(mNativeContentViewCore, override, reloadOnChange);
2191 public void clearSslPreferences() {
2192 if (mNativeContentViewCore != 0) nativeClearSslPreferences(mNativeContentViewCore);
2195 private void hideTextHandles() {
2196 mHasSelection = false;
2197 mHasInsertion = false;
2198 if (mNativeContentViewCore != 0) nativeHideTextHandles(mNativeContentViewCore);
2202 * Shows the IME if the focused widget could accept text input.
2204 public void showImeIfNeeded() {
2205 assert mWebContents != null;
2206 mWebContents.showImeIfNeeded();
2210 * Hides the IME if the containerView is the active view for IME.
2212 public void hideImeIfNeeded() {
2213 // Hide input method window from the current view synchronously
2214 // because ImeAdapter does so asynchronouly with a delay, and
2215 // by the time when ImeAdapter dismisses the input, the
2216 // containerView may have lost focus.
2217 // We cannot trust ContentViewClient#onImeStateChangeRequested to
2218 // hide the input window because it has an empty default implementation.
2219 // So we need to explicitly hide the input method window here.
2220 if (mInputMethodManagerWrapper.isActive(mContainerView)) {
2221 mInputMethodManagerWrapper.hideSoftInputFromWindow(
2222 mContainerView.getWindowToken(), 0, null);
2224 getContentViewClient().onImeStateChangeRequested(false);
2227 @SuppressWarnings("unused")
2229 private void updateFrameInfo(
2230 float scrollOffsetX, float scrollOffsetY,
2231 float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
2232 float contentWidth, float contentHeight,
2233 float viewportWidth, float viewportHeight,
2234 float controlsOffsetYCss, float contentOffsetYCss,
2235 float overdrawBottomHeightCss) {
2236 TraceEvent.begin("ContentViewCore:updateFrameInfo");
2237 // Adjust contentWidth/Height to be always at least as big as
2238 // the actual viewport (as set by onSizeChanged).
2239 final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2240 contentWidth = Math.max(contentWidth,
2241 mViewportWidthPix / (deviceScale * pageScaleFactor));
2242 contentHeight = Math.max(contentHeight,
2243 mViewportHeightPix / (deviceScale * pageScaleFactor));
2244 final float contentOffsetYPix = mRenderCoordinates.fromDipToPix(contentOffsetYCss);
2246 final boolean contentSizeChanged =
2247 contentWidth != mRenderCoordinates.getContentWidthCss()
2248 || contentHeight != mRenderCoordinates.getContentHeightCss();
2249 final boolean scaleLimitsChanged =
2250 minPageScaleFactor != mRenderCoordinates.getMinPageScaleFactor()
2251 || maxPageScaleFactor != mRenderCoordinates.getMaxPageScaleFactor();
2252 final boolean pageScaleChanged =
2253 pageScaleFactor != mRenderCoordinates.getPageScaleFactor();
2254 final boolean scrollChanged =
2256 || scrollOffsetX != mRenderCoordinates.getScrollX()
2257 || scrollOffsetY != mRenderCoordinates.getScrollY();
2258 final boolean contentOffsetChanged =
2259 contentOffsetYPix != mRenderCoordinates.getContentOffsetYPix();
2261 final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
2262 final boolean needUpdateZoomControls = scaleLimitsChanged || scrollChanged;
2264 if (needHidePopupZoomer) mPopupZoomer.hide(true);
2266 if (scrollChanged) {
2267 mContainerViewInternals.onScrollChanged(
2268 (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetX),
2269 (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetY),
2270 (int) mRenderCoordinates.getScrollXPix(),
2271 (int) mRenderCoordinates.getScrollYPix());
2274 mRenderCoordinates.updateFrameInfo(
2275 scrollOffsetX, scrollOffsetY,
2276 contentWidth, contentHeight,
2277 viewportWidth, viewportHeight,
2278 pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
2281 if (scrollChanged || contentOffsetChanged) {
2282 for (mGestureStateListenersIterator.rewind();
2283 mGestureStateListenersIterator.hasNext();) {
2284 mGestureStateListenersIterator.next().onScrollOffsetOrExtentChanged(
2285 computeVerticalScrollOffset(),
2286 computeVerticalScrollExtent());
2290 if (needUpdateZoomControls) mZoomControlsDelegate.updateZoomControls();
2292 // Update offsets for fullscreen.
2293 final float controlsOffsetPix = controlsOffsetYCss * deviceScale;
2294 final float overdrawBottomHeightPix = overdrawBottomHeightCss * deviceScale;
2295 getContentViewClient().onOffsetsForFullscreenChanged(
2296 controlsOffsetPix, contentOffsetYPix, overdrawBottomHeightPix);
2298 if (mBrowserAccessibilityManager != null) {
2299 mBrowserAccessibilityManager.notifyFrameInfoInitialized();
2301 TraceEvent.end("ContentViewCore:updateFrameInfo");
2305 private void updateImeAdapter(long nativeImeAdapterAndroid, int textInputType,
2306 int textInputFlags, String text, int selectionStart, int selectionEnd,
2307 int compositionStart, int compositionEnd, boolean showImeIfNeeded,
2308 boolean isNonImeChange) {
2310 mFocusedNodeEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
2311 if (!mFocusedNodeEditable) hidePastePopup();
2313 mImeAdapter.updateKeyboardVisibility(
2314 nativeImeAdapterAndroid, textInputType, textInputFlags, showImeIfNeeded);
2316 if (mInputConnection != null) {
2317 mInputConnection.updateState(text, selectionStart, selectionEnd, compositionStart,
2318 compositionEnd, isNonImeChange);
2321 if (mActionMode != null) mActionMode.invalidate();
2325 @SuppressWarnings("unused")
2327 private void setTitle(String title) {
2328 getContentViewClient().onUpdateTitle(title);
2332 * Called (from native) when the <select> popup needs to be shown.
2333 * @param items Items to show.
2334 * @param enabled POPUP_ITEM_TYPEs for items.
2335 * @param multiple Whether the popup menu should support multi-select.
2336 * @param selectedIndices Indices of selected items.
2338 @SuppressWarnings("unused")
2340 private void showSelectPopup(Rect bounds, String[] items, int[] enabled, boolean multiple,
2341 int[] selectedIndices) {
2342 if (mContainerView.getParent() == null || mContainerView.getVisibility() != View.VISIBLE) {
2343 selectPopupMenuItems(null);
2347 assert items.length == enabled.length;
2348 List<SelectPopupItem> popupItems = new ArrayList<SelectPopupItem>();
2349 for (int i = 0; i < items.length; i++) {
2350 popupItems.add(new SelectPopupItem(items[i], enabled[i]));
2353 if (DeviceFormFactor.isTablet(mContext) && !multiple) {
2354 mSelectPopup = new SelectPopupDropdown(this, popupItems, bounds, selectedIndices);
2356 mSelectPopup = new SelectPopupDialog(this, popupItems, multiple, selectedIndices);
2358 mSelectPopup.show();
2362 * Called when the <select> popup needs to be hidden.
2365 private void hideSelectPopup() {
2366 if (mSelectPopup != null) mSelectPopup.hide();
2370 * @return The visible select popup being shown.
2372 public SelectPopup getSelectPopupForTest() {
2373 return mSelectPopup;
2376 @SuppressWarnings("unused")
2378 private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
2379 mPopupZoomer.setBitmap(zoomedBitmap);
2380 mPopupZoomer.show(targetRect);
2383 @SuppressWarnings("unused")
2385 private TouchEventSynthesizer createTouchEventSynthesizer() {
2386 return new TouchEventSynthesizer(this);
2389 @SuppressWarnings("unused")
2391 private PopupTouchHandleDrawable createPopupTouchHandleDrawable() {
2392 if (mTouchHandleDelegate == null) {
2393 mTouchHandleDelegate = new PopupTouchHandleDrawableDelegate() {
2394 public View getParent() {
2395 return getContainerView();
2398 public PositionObserver getParentPositionObserver() {
2399 return mPositionObserver;
2402 public boolean onTouchHandleEvent(MotionEvent event) {
2403 final boolean isTouchHandleEvent = true;
2404 return onTouchEventImpl(event, isTouchHandleEvent);
2408 return new PopupTouchHandleDrawable(mTouchHandleDelegate);
2411 @SuppressWarnings("unused")
2413 private void onSelectionChanged(String text) {
2414 mLastSelectedText = text;
2415 getContentViewClient().onSelectionChanged(text);
2418 @SuppressWarnings("unused")
2420 private void showPastePopupWithFeedback(int xDip, int yDip) {
2421 // TODO(jdduke): Remove this when there is a better signal that long press caused
2422 // showing of the paste popup. See http://crbug.com/150151.
2423 if (showPastePopup(xDip, yDip)) {
2424 mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2428 private boolean showPastePopup(int xDip, int yDip) {
2429 if (!mHasInsertion || !canPaste()) return false;
2430 final float contentOffsetYPix = mRenderCoordinates.getContentOffsetYPix();
2431 getPastePopup().showAt(
2432 (int) mRenderCoordinates.fromDipToPix(xDip),
2433 (int) (mRenderCoordinates.fromDipToPix(yDip) + contentOffsetYPix));
2437 private PastePopupMenu getPastePopup() {
2438 if (mPastePopupMenu == null) {
2439 mPastePopupMenu = new PastePopupMenu(getContainerView(),
2440 new PastePopupMenuDelegate() {
2441 public void paste() {
2442 mImeAdapter.paste();
2447 return mPastePopupMenu;
2450 private boolean canPaste() {
2451 if (!mFocusedNodeEditable) return false;
2452 return ((ClipboardManager) mContext.getSystemService(
2453 Context.CLIPBOARD_SERVICE)).hasPrimaryClip();
2456 @SuppressWarnings("unused")
2458 private void onRenderProcessChange() {
2463 * Attaches the native ImeAdapter object to the java ImeAdapter to allow communication via JNI.
2465 public void attachImeAdapter() {
2466 if (mImeAdapter != null && mNativeContentViewCore != 0) {
2467 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2472 * @see View#hasFocus()
2475 public boolean hasFocus() {
2476 return mContainerView.hasFocus();
2480 * Checks whether the ContentViewCore can be zoomed in.
2482 * @return True if the ContentViewCore can be zoomed in.
2484 // This method uses the term 'zoom' for legacy reasons, but relates
2485 // to what chrome calls the 'page scale factor'.
2486 public boolean canZoomIn() {
2487 final float zoomInExtent = mRenderCoordinates.getMaxPageScaleFactor()
2488 - mRenderCoordinates.getPageScaleFactor();
2489 return zoomInExtent > ZOOM_CONTROLS_EPSILON;
2493 * Checks whether the ContentViewCore can be zoomed out.
2495 * @return True if the ContentViewCore can be zoomed out.
2497 // This method uses the term 'zoom' for legacy reasons, but relates
2498 // to what chrome calls the 'page scale factor'.
2499 public boolean canZoomOut() {
2500 final float zoomOutExtent = mRenderCoordinates.getPageScaleFactor()
2501 - mRenderCoordinates.getMinPageScaleFactor();
2502 return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
2506 * Zooms in the ContentViewCore by 25% (or less if that would result in
2507 * zooming in more than possible).
2509 * @return True if there was a zoom change, false otherwise.
2511 // This method uses the term 'zoom' for legacy reasons, but relates
2512 // to what chrome calls the 'page scale factor'.
2513 public boolean zoomIn() {
2517 return pinchByDelta(1.25f);
2521 * Zooms out the ContentViewCore by 20% (or less if that would result in
2522 * zooming out more than possible).
2524 * @return True if there was a zoom change, false otherwise.
2526 // This method uses the term 'zoom' for legacy reasons, but relates
2527 // to what chrome calls the 'page scale factor'.
2528 public boolean zoomOut() {
2529 if (!canZoomOut()) {
2532 return pinchByDelta(0.8f);
2536 * Resets the zoom factor of the ContentViewCore.
2538 * @return True if there was a zoom change, false otherwise.
2540 // This method uses the term 'zoom' for legacy reasons, but relates
2541 // to what chrome calls the 'page scale factor'.
2542 public boolean zoomReset() {
2543 // The page scale factor is initialized to mNativeMinimumScale when
2544 // the page finishes loading. Thus sets it back to mNativeMinimumScale.
2545 if (!canZoomOut()) return false;
2546 return pinchByDelta(
2547 mRenderCoordinates.getMinPageScaleFactor()
2548 / mRenderCoordinates.getPageScaleFactor());
2552 * Simulate a pinch zoom gesture.
2554 * @param delta the factor by which the current page scale should be multiplied by.
2555 * @return whether the gesture was sent.
2557 public boolean pinchByDelta(float delta) {
2558 if (mNativeContentViewCore == 0) return false;
2560 long timeMs = SystemClock.uptimeMillis();
2561 int xPix = getViewportWidthPix() / 2;
2562 int yPix = getViewportHeightPix() / 2;
2564 nativePinchBegin(mNativeContentViewCore, timeMs, xPix, yPix);
2565 nativePinchBy(mNativeContentViewCore, timeMs, xPix, yPix, delta);
2566 nativePinchEnd(mNativeContentViewCore, timeMs);
2572 * Invokes the graphical zoom picker widget for this ContentView.
2574 public void invokeZoomPicker() {
2575 mZoomControlsDelegate.invokeZoomPicker();
2579 * Enables or disables inspection of JavaScript objects added via
2580 * {@link #addJavascriptInterface(Object, String)} by means of Object.keys() method and
2581 * "for .. in" loop. Being able to inspect JavaScript objects is useful
2582 * when debugging hybrid Android apps, but can't be enabled for legacy applications due
2583 * to compatibility risks.
2585 * @param allow Whether to allow JavaScript objects inspection.
2587 public void setAllowJavascriptInterfacesInspection(boolean allow) {
2588 nativeSetAllowJavascriptInterfacesInspection(mNativeContentViewCore, allow);
2592 * Returns JavaScript interface objects previously injected via
2593 * {@link #addJavascriptInterface(Object, String)}.
2595 * @return the mapping of names to interface objects and corresponding annotation classes
2597 public Map<String, Pair<Object, Class>> getJavascriptInterfaces() {
2598 return mJavaScriptInterfaces;
2602 * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
2603 * and automatically pass in {@link JavascriptInterface} as the required annotation.
2605 * @param object The Java object to inject into the ContentViewCore's JavaScript context. Null
2606 * values are ignored.
2607 * @param name The name used to expose the instance in JavaScript.
2609 public void addJavascriptInterface(Object object, String name) {
2610 addPossiblyUnsafeJavascriptInterface(object, name, JavascriptInterface.class);
2614 * This method injects the supplied Java object into the ContentViewCore.
2615 * The object is injected into the JavaScript context of the main frame,
2616 * using the supplied name. This allows the Java object to be accessed from
2617 * JavaScript. Note that that injected objects will not appear in
2618 * JavaScript until the page is next (re)loaded. For example:
2619 * <pre> view.addJavascriptInterface(new Object(), "injectedObject");
2620 * view.loadData("<!DOCTYPE html><title></title>", "text/html", null);
2621 * view.loadUrl("javascript:alert(injectedObject.toString())");</pre>
2622 * <p><strong>IMPORTANT:</strong>
2624 * <li> addJavascriptInterface() can be used to allow JavaScript to control
2625 * the host application. This is a powerful feature, but also presents a
2626 * security risk. Use of this method in a ContentViewCore containing
2627 * untrusted content could allow an attacker to manipulate the host
2628 * application in unintended ways, executing Java code with the permissions
2629 * of the host application. Use extreme care when using this method in a
2630 * ContentViewCore which could contain untrusted content. Particular care
2631 * should be taken to avoid unintentional access to inherited methods, such
2632 * as {@link Object#getClass()}. To prevent access to inherited methods,
2633 * pass an annotation for {@code requiredAnnotation}. This will ensure
2634 * that only methods with {@code requiredAnnotation} are exposed to the
2635 * Javascript layer. {@code requiredAnnotation} will be passed to all
2636 * subsequently injected Java objects if any methods return an object. This
2637 * means the same restrictions (or lack thereof) will apply. Alternatively,
2638 * {@link #addJavascriptInterface(Object, String)} can be called, which
2639 * automatically uses the {@link JavascriptInterface} annotation.
2640 * <li> JavaScript interacts with Java objects on a private, background
2641 * thread of the ContentViewCore. Care is therefore required to maintain
2642 * thread safety.</li>
2645 * @param object The Java object to inject into the
2646 * ContentViewCore's JavaScript context. Null
2647 * values are ignored.
2648 * @param name The name used to expose the instance in
2650 * @param requiredAnnotation Restrict exposed methods to ones with this
2651 * annotation. If {@code null} all methods are
2655 public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
2656 Class<? extends Annotation> requiredAnnotation) {
2657 if (mNativeContentViewCore != 0 && object != null) {
2658 mJavaScriptInterfaces.put(name, new Pair<Object, Class>(object, requiredAnnotation));
2659 nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation);
2664 * Removes a previously added JavaScript interface with the given name.
2666 * @param name The name of the interface to remove.
2668 public void removeJavascriptInterface(String name) {
2669 mJavaScriptInterfaces.remove(name);
2670 if (mNativeContentViewCore != 0) {
2671 nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2676 * Return the current scale of the ContentView.
2677 * @return The current page scale factor.
2679 public float getScale() {
2680 return mRenderCoordinates.getPageScaleFactor();
2684 * If the view is ready to draw contents to the screen. In hardware mode,
2685 * the initialization of the surface texture may not occur until after the
2686 * view has been added to the layout. This method will return {@code true}
2687 * once the texture is actually ready.
2689 public boolean isReady() {
2690 assert mWebContents != null;
2691 return mWebContents.isReady();
2695 private void startContentIntent(String contentUrl) {
2696 getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2700 public void onAccessibilityStateChanged(boolean enabled) {
2701 setAccessibilityState(enabled);
2705 * Determines whether or not this ContentViewCore can handle this accessibility action.
2706 * @param action The action to perform.
2707 * @return Whether or not this action is supported.
2709 public boolean supportsAccessibilityAction(int action) {
2710 return mAccessibilityInjector.supportsAccessibilityAction(action);
2714 * Attempts to perform an accessibility action on the web content. If the accessibility action
2715 * cannot be processed, it returns {@code null}, allowing the caller to know to call the
2716 * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value.
2717 * Otherwise the return value from this method should be used.
2718 * @param action The action to perform.
2719 * @param arguments Optional action arguments.
2720 * @return Whether the action was performed or {@code null} if the call should be delegated to
2721 * the super {@link View} class.
2723 public boolean performAccessibilityAction(int action, Bundle arguments) {
2724 if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2725 return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2732 * Set the BrowserAccessibilityManager, used for native accessibility
2733 * (not script injection). This is only set when system accessibility
2735 * @param manager The new BrowserAccessibilityManager.
2737 public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
2738 mBrowserAccessibilityManager = manager;
2742 * Get the BrowserAccessibilityManager, used for native accessibility
2743 * (not script injection). This will return null when system accessibility
2745 * @return This view's BrowserAccessibilityManager.
2747 public BrowserAccessibilityManager getBrowserAccessibilityManager() {
2748 return mBrowserAccessibilityManager;
2752 * If native accessibility (not script injection) is enabled, and if this is
2753 * running on JellyBean or later, returns an AccessibilityNodeProvider that
2754 * implements native accessibility for this view. Returns null otherwise.
2755 * Lazily initializes native accessibility here if it's allowed.
2756 * @return The AccessibilityNodeProvider, if available, or null otherwise.
2758 public AccessibilityNodeProvider getAccessibilityNodeProvider() {
2759 if (mBrowserAccessibilityManager != null) {
2760 return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
2763 if (mNativeAccessibilityAllowed &&
2764 !mNativeAccessibilityEnabled &&
2765 mNativeContentViewCore != 0 &&
2766 Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
2767 mNativeAccessibilityEnabled = true;
2768 nativeSetAccessibilityEnabled(mNativeContentViewCore, true);
2775 * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
2777 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2778 // Note: this is only used by the script-injecting accessibility code.
2779 mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
2783 * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
2785 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
2786 // Note: this is only used by the script-injecting accessibility code.
2787 event.setClassName(this.getClass().getName());
2789 // Identify where the top-left of the screen currently points to.
2790 event.setScrollX(mRenderCoordinates.getScrollXPixInt());
2791 event.setScrollY(mRenderCoordinates.getScrollYPixInt());
2793 // The maximum scroll values are determined by taking the content dimensions and
2794 // subtracting off the actual dimensions of the ChromeView.
2795 int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt());
2796 int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt());
2797 event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0);
2799 // Setting the maximum scroll values requires API level 15 or higher.
2800 final int SDK_VERSION_REQUIRED_TO_SET_SCROLL = 15;
2801 if (Build.VERSION.SDK_INT >= SDK_VERSION_REQUIRED_TO_SET_SCROLL) {
2802 event.setMaxScrollX(maxScrollXPix);
2803 event.setMaxScrollY(maxScrollYPix);
2808 * Returns whether accessibility script injection is enabled on the device
2810 public boolean isDeviceAccessibilityScriptInjectionEnabled() {
2812 // On JellyBean and higher, native accessibility is the default so script
2813 // injection is only allowed if enabled via a flag.
2814 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&
2815 !CommandLine.getInstance().hasSwitch(
2816 ContentSwitches.ENABLE_ACCESSIBILITY_SCRIPT_INJECTION)) {
2820 if (!mContentSettings.getJavaScriptEnabled()) {
2824 int result = getContext().checkCallingOrSelfPermission(
2825 android.Manifest.permission.INTERNET);
2826 if (result != PackageManager.PERMISSION_GRANTED) {
2830 Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
2831 field.setAccessible(true);
2832 String accessibilityScriptInjection = (String) field.get(null);
2833 ContentResolver contentResolver = getContext().getContentResolver();
2835 if (mAccessibilityScriptInjectionObserver == null) {
2836 ContentObserver contentObserver = new ContentObserver(new Handler()) {
2838 public void onChange(boolean selfChange, Uri uri) {
2839 setAccessibilityState(mAccessibilityManager.isEnabled());
2842 contentResolver.registerContentObserver(
2843 Settings.Secure.getUriFor(accessibilityScriptInjection),
2846 mAccessibilityScriptInjectionObserver = contentObserver;
2849 return Settings.Secure.getInt(contentResolver, accessibilityScriptInjection, 0) == 1;
2850 } catch (NoSuchFieldException e) {
2851 // Do nothing, default to false.
2852 } catch (IllegalAccessException e) {
2853 // Do nothing, default to false.
2859 * Returns whether or not accessibility injection is being used.
2861 public boolean isInjectingAccessibilityScript() {
2862 return mAccessibilityInjector.accessibilityIsAvailable();
2866 * Returns true if accessibility is on and touch exploration is enabled.
2868 public boolean isTouchExplorationEnabled() {
2869 return mTouchExplorationEnabled;
2873 * Turns browser accessibility on or off.
2874 * If |state| is |false|, this turns off both native and injected accessibility.
2875 * Otherwise, if accessibility script injection is enabled, this will enable the injected
2876 * accessibility scripts. Native accessibility is enabled on demand.
2878 public void setAccessibilityState(boolean state) {
2880 setInjectedAccessibility(false);
2881 mNativeAccessibilityAllowed = false;
2882 mTouchExplorationEnabled = false;
2884 boolean useScriptInjection = isDeviceAccessibilityScriptInjectionEnabled();
2885 setInjectedAccessibility(useScriptInjection);
2886 mNativeAccessibilityAllowed = !useScriptInjection;
2887 mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
2892 * Enable or disable injected accessibility features
2894 public void setInjectedAccessibility(boolean enabled) {
2895 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
2896 mAccessibilityInjector.setScriptEnabled(enabled);
2900 * Stop any TTS notifications that are currently going on.
2902 public void stopCurrentAccessibilityNotifications() {
2903 mAccessibilityInjector.onPageLostFocus();
2907 * Return whether or not we should set accessibility focus on page load.
2909 public boolean shouldSetAccessibilityFocusOnPageLoad() {
2910 return mShouldSetAccessibilityFocusOnPageLoad;
2914 * Return whether or not we should set accessibility focus on page load.
2915 * This only applies if an accessibility service like TalkBack is running.
2916 * This is desirable behavior for a browser window, but not for an embedded
2919 public void setShouldSetAccessibilityFocusOnPageLoad(boolean on) {
2920 mShouldSetAccessibilityFocusOnPageLoad = on;
2924 * Inform WebKit that Fullscreen mode has been exited by the user.
2926 public void exitFullscreen() {
2927 assert mWebContents != null;
2928 mWebContents.exitFullscreen();
2932 * Changes whether hiding the top controls is enabled.
2934 * @param enableHiding Whether hiding the top controls should be enabled or not.
2935 * @param enableShowing Whether showing the top controls should be enabled or not.
2936 * @param animate Whether the transition should be animated or not.
2938 public void updateTopControlsState(boolean enableHiding, boolean enableShowing,
2940 assert mWebContents != null;
2941 mWebContents.updateTopControlsState(
2942 enableHiding, enableShowing, animate);
2946 * Callback factory method for nativeGetNavigationHistory().
2949 private void addToNavigationHistory(Object history, int index, String url, String virtualUrl,
2950 String originalUrl, String title, Bitmap favicon) {
2951 NavigationEntry entry = new NavigationEntry(
2952 index, url, virtualUrl, originalUrl, title, favicon);
2953 ((NavigationHistory) history).addEntry(entry);
2957 * Get a copy of the navigation history of the view.
2959 public NavigationHistory getNavigationHistory() {
2960 NavigationHistory history = new NavigationHistory();
2961 if (mNativeContentViewCore != 0) {
2962 int currentIndex = nativeGetNavigationHistory(mNativeContentViewCore, history);
2963 history.setCurrentEntryIndex(currentIndex);
2969 public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
2970 NavigationHistory history = new NavigationHistory();
2971 if (mNativeContentViewCore != 0) {
2972 nativeGetDirectedNavigationHistory(
2973 mNativeContentViewCore, history, isForward, itemLimit);
2979 * @return The original request URL for the current navigation entry, or null if there is no
2982 public String getOriginalUrlForActiveNavigationEntry() {
2983 if (mNativeContentViewCore != 0) {
2984 return nativeGetOriginalUrlForActiveNavigationEntry(mNativeContentViewCore);
2990 * @return The cached copy of render positions and scales.
2992 public RenderCoordinates getRenderCoordinates() {
2993 return mRenderCoordinates;
2997 private static Rect createRect(int x, int y, int right, int bottom) {
2998 return new Rect(x, y, right, bottom);
3001 public void extractSmartClipData(int x, int y, int width, int height) {
3002 if (mNativeContentViewCore != 0) {
3003 x += mSmartClipOffsetX;
3004 y += mSmartClipOffsetY;
3005 nativeExtractSmartClipData(mNativeContentViewCore, x, y, width, height);
3010 * Set offsets for smart clip.
3012 * <p>This should be called if there is a viewport change introduced by,
3013 * e.g., show and hide of a location bar.
3015 * @param offsetX Offset for X position.
3016 * @param offsetY Offset for Y position.
3018 public void setSmartClipOffsets(int offsetX, int offsetY) {
3019 mSmartClipOffsetX = offsetX;
3020 mSmartClipOffsetY = offsetY;
3024 private void onSmartClipDataExtracted(String text, String html, Rect clipRect) {
3025 if (mSmartClipDataListener != null ) {
3026 mSmartClipDataListener.onSmartClipDataExtracted(text, html, clipRect);
3030 public void setSmartClipDataListener(SmartClipDataListener listener) {
3031 mSmartClipDataListener = listener;
3034 public void setBackgroundOpaque(boolean opaque) {
3035 if (mNativeContentViewCore != 0) {
3036 nativeSetBackgroundOpaque(mNativeContentViewCore, opaque);
3041 * Offer a long press gesture to the embedding View, primarily for WebView compatibility.
3043 * @return true if the embedder handled the event.
3045 private boolean offerLongPressToEmbedder() {
3046 return mContainerView.performLongClick();
3050 * Reset scroll and fling accounting, notifying listeners as appropriate.
3051 * This is useful as a failsafe when the input stream may have been interruped.
3053 private void resetScrollInProgress() {
3054 if (!isScrollInProgress()) return;
3056 final boolean touchScrollInProgress = mTouchScrollInProgress;
3057 final int potentiallyActiveFlingCount = mPotentiallyActiveFlingCount;
3059 mTouchScrollInProgress = false;
3060 mPotentiallyActiveFlingCount = 0;
3062 if (touchScrollInProgress) updateGestureStateListener(GestureEventType.SCROLL_END);
3063 if (potentiallyActiveFlingCount > 0) updateGestureStateListener(GestureEventType.FLING_END);
3066 private native long nativeInit(long webContentsPtr,
3067 long viewAndroidPtr, long windowAndroidPtr, HashSet<Object> retainedObjectSet);
3070 private ContentVideoViewClient getContentVideoViewClient() {
3071 return getContentViewClient().getContentVideoViewClient();
3075 private boolean shouldBlockMediaRequest(String url) {
3076 return getContentViewClient().shouldBlockMediaRequest(url);
3080 private void onNativeFlingStopped() {
3081 // Note that mTouchScrollInProgress should normally be false at this
3082 // point, but we reset it anyway as another failsafe.
3083 mTouchScrollInProgress = false;
3084 if (mPotentiallyActiveFlingCount <= 0) return;
3085 mPotentiallyActiveFlingCount--;
3086 updateGestureStateListener(GestureEventType.FLING_END);
3090 public void onScreenOrientationChanged(int orientation) {
3091 sendOrientationChangeEvent(orientation);
3094 public void resumeResponseDeferredAtStart() {
3095 assert mWebContents != null;
3096 mWebContents.resumeResponseDeferredAtStart();
3100 * Set whether the ContentViewCore requires the WebContents to be fullscreen in order to lock
3101 * the screen orientation.
3103 public void setFullscreenRequiredForOrientationLock(boolean value) {
3104 mFullscreenRequiredForOrientationLock = value;
3108 private boolean isFullscreenRequiredForOrientationLock() {
3109 return mFullscreenRequiredForOrientationLock;
3112 private native WebContents nativeGetWebContentsAndroid(long nativeContentViewCoreImpl);
3114 private native void nativeOnJavaContentViewCoreDestroyed(long nativeContentViewCoreImpl);
3116 private native void nativeLoadUrl(
3117 long nativeContentViewCoreImpl,
3123 int uaOverrideOption,
3124 String extraHeaders,
3126 String baseUrlForDataUrl,
3127 String virtualUrlForDataUrl,
3128 boolean canLoadLocalResources,
3129 boolean isRendererInitiated);
3131 private native void nativeSetFocus(long nativeContentViewCoreImpl, boolean focused);
3133 private native void nativeSendOrientationChangeEvent(
3134 long nativeContentViewCoreImpl, int orientation);
3136 // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
3137 private native boolean nativeOnTouchEvent(
3138 long nativeContentViewCoreImpl, MotionEvent event,
3139 long timeMs, int action, int pointerCount, int historySize, int actionIndex,
3140 float x0, float y0, float x1, float y1,
3141 int pointerId0, int pointerId1,
3142 float touchMajor0, float touchMajor1,
3143 float rawX, float rawY,
3144 int androidToolType0, int androidToolType1, int androidButtonState,
3145 boolean isTouchHandleEvent);
3147 private native int nativeSendMouseMoveEvent(
3148 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3150 private native int nativeSendMouseWheelEvent(
3151 long nativeContentViewCoreImpl, long timeMs, float x, float y, float verticalAxis);
3153 private native void nativeScrollBegin(
3154 long nativeContentViewCoreImpl, long timeMs, float x, float y, float hintX,
3157 private native void nativeScrollEnd(long nativeContentViewCoreImpl, long timeMs);
3159 private native void nativeScrollBy(
3160 long nativeContentViewCoreImpl, long timeMs, float x, float y,
3161 float deltaX, float deltaY);
3163 private native void nativeFlingStart(
3164 long nativeContentViewCoreImpl, long timeMs, float x, float y, float vx, float vy);
3166 private native void nativeFlingCancel(long nativeContentViewCoreImpl, long timeMs);
3168 private native void nativeSingleTap(
3169 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3171 private native void nativeDoubleTap(
3172 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3174 private native void nativeLongPress(
3175 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3177 private native void nativePinchBegin(
3178 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3180 private native void nativePinchEnd(long nativeContentViewCoreImpl, long timeMs);
3182 private native void nativePinchBy(long nativeContentViewCoreImpl, long timeMs,
3183 float anchorX, float anchorY, float deltaScale);
3185 private native void nativeSelectBetweenCoordinates(
3186 long nativeContentViewCoreImpl, float x1, float y1, float x2, float y2);
3188 private native void nativeMoveCaret(long nativeContentViewCoreImpl, float x, float y);
3190 private native void nativeHideTextHandles(long nativeContentViewCoreImpl);
3192 private native void nativeResetGestureDetection(long nativeContentViewCoreImpl);
3194 private native void nativeSetDoubleTapSupportEnabled(
3195 long nativeContentViewCoreImpl, boolean enabled);
3197 private native void nativeSetMultiTouchZoomSupportEnabled(
3198 long nativeContentViewCoreImpl, boolean enabled);
3200 private native void nativeSelectPopupMenuItems(long nativeContentViewCoreImpl, int[] indices);
3202 private native void nativeClearHistory(long nativeContentViewCoreImpl);
3204 private native void nativePostMessageToFrame(long nativeContentViewCoreImpl, String frameId,
3205 String message, String sourceOrigin, String targetOrigin);
3207 private native long nativeGetNativeImeAdapter(long nativeContentViewCoreImpl);
3209 private native int nativeGetCurrentRenderProcessId(long nativeContentViewCoreImpl);
3211 private native void nativeSetUseDesktopUserAgent(long nativeContentViewCoreImpl,
3212 boolean enabled, boolean reloadOnChange);
3214 private native boolean nativeGetUseDesktopUserAgent(long nativeContentViewCoreImpl);
3216 private native void nativeClearSslPreferences(long nativeContentViewCoreImpl);
3218 private native void nativeSetAllowJavascriptInterfacesInspection(
3219 long nativeContentViewCoreImpl, boolean allow);
3221 private native void nativeAddJavascriptInterface(long nativeContentViewCoreImpl, Object object,
3222 String name, Class requiredAnnotation);
3224 private native void nativeRemoveJavascriptInterface(long nativeContentViewCoreImpl,
3227 private native int nativeGetNavigationHistory(long nativeContentViewCoreImpl, Object context);
3229 private native void nativeGetDirectedNavigationHistory(long nativeContentViewCoreImpl,
3230 Object context, boolean isForward, int maxEntries);
3232 private native String nativeGetOriginalUrlForActiveNavigationEntry(
3233 long nativeContentViewCoreImpl);
3235 private native void nativeWasResized(long nativeContentViewCoreImpl);
3237 private native void nativeSetAccessibilityEnabled(
3238 long nativeContentViewCoreImpl, boolean enabled);
3240 private native void nativeExtractSmartClipData(long nativeContentViewCoreImpl,
3241 int x, int y, int w, int h);
3243 private native void nativeSetBackgroundOpaque(long nativeContentViewCoreImpl, boolean opaque);