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 if (mWebContents != null) mWebContents.stop();
880 * Get the URL of the current page.
882 * @return The URL of the current page.
884 public String getUrl() {
885 return (mWebContents != null) ? mWebContents.getUrl() : null;
889 * Get the title of the current page.
891 * @return The title of the current page.
893 public String getTitle() {
894 return (mWebContents != null) ? mWebContents.getTitle() : null;
898 * Shows an interstitial page driven by the passed in delegate.
900 * @param url The URL being blocked by the interstitial.
901 * @param delegate The delegate handling the interstitial.
904 public void showInterstitialPage(
905 String url, InterstitialPageDelegateAndroid delegate) {
906 assert mWebContents != null;
907 mWebContents.showInterstitialPage(url, delegate.getNative());
911 * @return Whether the page is currently showing an interstitial, such as a bad HTTPS page.
913 public boolean isShowingInterstitialPage() {
914 assert mWebContents != null;
915 return mWebContents.isShowingInterstitialPage();
919 * @return Viewport width in physical pixels as set from onSizeChanged.
922 public int getViewportWidthPix() { return mViewportWidthPix; }
925 * @return Viewport height in physical pixels as set from onSizeChanged.
928 public int getViewportHeightPix() { return mViewportHeightPix; }
931 * @return Width of underlying physical surface.
934 public int getPhysicalBackingWidthPix() { return mPhysicalBackingWidthPix; }
937 * @return Height of underlying physical surface.
940 public int getPhysicalBackingHeightPix() { return mPhysicalBackingHeightPix; }
943 * @return Amount the output surface extends past the bottom of the window viewport.
946 public int getOverdrawBottomHeightPix() { return mOverdrawBottomHeightPix; }
949 * @return The amount to shrink the viewport relative to {@link #getViewportWidthPix()}.
952 public int getViewportSizeOffsetWidthPix() { return mViewportSizeOffsetWidthPix; }
955 * @return The amount to shrink the viewport relative to {@link #getViewportHeightPix()}.
958 public int getViewportSizeOffsetHeightPix() { return mViewportSizeOffsetHeightPix; }
961 * @see android.webkit.WebView#getContentHeight()
963 public float getContentHeightCss() {
964 return mRenderCoordinates.getContentHeightCss();
968 * @see android.webkit.WebView#getContentWidth()
970 public float getContentWidthCss() {
971 return mRenderCoordinates.getContentWidthCss();
974 // TODO(teddchoc): Remove all these navigation controller methods from here and have the
975 // embedders manage it.
977 * @return Whether the current WebContents has a previous navigation entry.
979 public boolean canGoBack() {
980 return mWebContents != null && mWebContents.getNavigationController().canGoBack();
984 * @return Whether the current WebContents has a navigation entry after the current one.
986 public boolean canGoForward() {
987 return mWebContents != null && mWebContents.getNavigationController().canGoForward();
991 * @param offset The offset into the navigation history.
992 * @return Whether we can move in history by given offset
994 public boolean canGoToOffset(int offset) {
995 return mWebContents != null &&
996 mWebContents.getNavigationController().canGoToOffset(offset);
1000 * Navigates to the specified offset from the "current entry". Does nothing if the offset is out
1002 * @param offset The offset into the navigation history.
1004 public void goToOffset(int offset) {
1005 if (mWebContents != null) mWebContents.getNavigationController().goToOffset(offset);
1009 public void goToNavigationIndex(int index) {
1010 if (mWebContents != null) {
1011 mWebContents.getNavigationController().goToNavigationIndex(index);
1016 * Goes to the navigation entry before the current one.
1018 public void goBack() {
1019 if (mWebContents != null) mWebContents.getNavigationController().goBack();
1023 * Goes to the navigation entry following the current one.
1025 public void goForward() {
1026 if (mWebContents != null) mWebContents.getNavigationController().goForward();
1030 * Loads the current navigation if there is a pending lazy load (after tab restore).
1032 public void loadIfNecessary() {
1033 if (mWebContents != null) mWebContents.getNavigationController().loadIfNecessary();
1037 * Requests the current navigation to be loaded upon the next call to loadIfNecessary().
1039 public void requestRestoreLoad() {
1040 if (mWebContents != null) mWebContents.getNavigationController().requestRestoreLoad();
1044 * Reload the current page.
1046 public void reload(boolean checkForRepost) {
1047 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1048 if (mWebContents != null) mWebContents.getNavigationController().reload(checkForRepost);
1052 * Reload the current page, ignoring the contents of the cache.
1054 public void reloadIgnoringCache(boolean checkForRepost) {
1055 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1056 if (mWebContents != null) mWebContents.getNavigationController().reloadIgnoringCache(
1061 * Cancel the pending reload.
1063 public void cancelPendingReload() {
1064 if (mWebContents != null) mWebContents.getNavigationController().cancelPendingReload();
1068 * Continue the pending reload.
1070 public void continuePendingReload() {
1071 if (mWebContents != null) mWebContents.getNavigationController().continuePendingReload();
1075 * Clears the ContentViewCore's page history in both the backwards and
1076 * forwards directions.
1078 public void clearHistory() {
1079 if (mNativeContentViewCore != 0) nativeClearHistory(mNativeContentViewCore);
1083 * @return The selected text (empty if no text selected).
1085 public String getSelectedText() {
1086 return mHasSelection ? mLastSelectedText : "";
1090 * @return Whether the current selection is editable (false if no text selected).
1092 public boolean isSelectionEditable() {
1093 return mHasSelection ? mFocusedNodeEditable : false;
1097 * @return Whether the current focused node is editable.
1099 public boolean isFocusedNodeEditable() {
1100 return mFocusedNodeEditable;
1103 // End FrameLayout overrides.
1106 * @see View#onTouchEvent(MotionEvent)
1108 public boolean onTouchEvent(MotionEvent event) {
1109 final boolean isTouchHandleEvent = false;
1110 return onTouchEventImpl(event, isTouchHandleEvent);
1113 private boolean onTouchEventImpl(MotionEvent event, boolean isTouchHandleEvent) {
1114 TraceEvent.begin("onTouchEvent");
1116 int eventAction = event.getActionMasked();
1118 if (eventAction == MotionEvent.ACTION_DOWN) {
1119 cancelRequestToScrollFocusedEditableNodeIntoView();
1122 if (SPenSupport.isSPenSupported(mContext))
1123 eventAction = SPenSupport.convertSPenEventAction(eventAction);
1124 if (!isValidTouchEventActionForNative(eventAction)) return false;
1126 if (mNativeContentViewCore == 0) return false;
1128 // A zero offset is quite common, in which case the unnecessary copy should be avoided.
1129 MotionEvent offset = null;
1130 if (mCurrentTouchOffsetX != 0 || mCurrentTouchOffsetY != 0) {
1131 offset = createOffsetMotionEvent(event);
1135 final int pointerCount = event.getPointerCount();
1136 final boolean consumed = nativeOnTouchEvent(mNativeContentViewCore, event,
1137 event.getEventTime(), eventAction,
1138 pointerCount, event.getHistorySize(), event.getActionIndex(),
1139 event.getX(), event.getY(),
1140 pointerCount > 1 ? event.getX(1) : 0,
1141 pointerCount > 1 ? event.getY(1) : 0,
1142 event.getPointerId(0), pointerCount > 1 ? event.getPointerId(1) : -1,
1143 event.getTouchMajor(), pointerCount > 1 ? event.getTouchMajor(1) : 0,
1144 event.getRawX(), event.getRawY(),
1145 event.getToolType(0),
1146 pointerCount > 1 ? event.getToolType(1) : MotionEvent.TOOL_TYPE_UNKNOWN,
1147 event.getButtonState(),
1148 isTouchHandleEvent);
1150 if (offset != null) offset.recycle();
1153 TraceEvent.end("onTouchEvent");
1157 private static boolean isValidTouchEventActionForNative(int eventAction) {
1158 // Only these actions have any effect on gesture detection. Other
1159 // actions have no corresponding WebTouchEvent type and may confuse the
1160 // touch pipline, so we ignore them entirely.
1161 return eventAction == MotionEvent.ACTION_DOWN
1162 || eventAction == MotionEvent.ACTION_UP
1163 || eventAction == MotionEvent.ACTION_CANCEL
1164 || eventAction == MotionEvent.ACTION_MOVE
1165 || eventAction == MotionEvent.ACTION_POINTER_DOWN
1166 || eventAction == MotionEvent.ACTION_POINTER_UP;
1169 public void setIgnoreRemainingTouchEvents() {
1170 resetGestureDetection();
1173 public boolean isScrollInProgress() {
1174 return mTouchScrollInProgress || mPotentiallyActiveFlingCount > 0;
1177 @SuppressWarnings("unused")
1179 private void onFlingStartEventConsumed(int vx, int vy) {
1180 mTouchScrollInProgress = false;
1181 mPotentiallyActiveFlingCount++;
1182 for (mGestureStateListenersIterator.rewind();
1183 mGestureStateListenersIterator.hasNext();) {
1184 mGestureStateListenersIterator.next().onFlingStartGesture(
1185 vx, vy, computeVerticalScrollOffset(), computeVerticalScrollExtent());
1189 @SuppressWarnings("unused")
1191 private void onFlingStartEventHadNoConsumer(int vx, int vy) {
1192 mTouchScrollInProgress = false;
1193 for (mGestureStateListenersIterator.rewind();
1194 mGestureStateListenersIterator.hasNext();) {
1195 mGestureStateListenersIterator.next().onUnhandledFlingStartEvent(vx, vy);
1199 @SuppressWarnings("unused")
1201 private void onFlingCancelEventAck() {
1202 updateGestureStateListener(GestureEventType.FLING_CANCEL);
1205 @SuppressWarnings("unused")
1207 private void onScrollBeginEventAck() {
1208 mTouchScrollInProgress = true;
1210 mZoomControlsDelegate.invokeZoomPicker();
1211 updateGestureStateListener(GestureEventType.SCROLL_START);
1214 @SuppressWarnings("unused")
1216 private void onScrollUpdateGestureConsumed() {
1217 mZoomControlsDelegate.invokeZoomPicker();
1218 for (mGestureStateListenersIterator.rewind();
1219 mGestureStateListenersIterator.hasNext();) {
1220 mGestureStateListenersIterator.next().onScrollUpdateGestureConsumed();
1224 @SuppressWarnings("unused")
1226 private void onScrollEndEventAck() {
1227 if (!mTouchScrollInProgress) return;
1228 mTouchScrollInProgress = false;
1229 updateGestureStateListener(GestureEventType.SCROLL_END);
1232 @SuppressWarnings("unused")
1234 private void onPinchBeginEventAck() {
1235 updateGestureStateListener(GestureEventType.PINCH_BEGIN);
1238 @SuppressWarnings("unused")
1240 private void onPinchEndEventAck() {
1241 updateGestureStateListener(GestureEventType.PINCH_END);
1244 @SuppressWarnings("unused")
1246 private void onSingleTapEventAck(boolean consumed, int x, int y) {
1247 for (mGestureStateListenersIterator.rewind();
1248 mGestureStateListenersIterator.hasNext();) {
1249 mGestureStateListenersIterator.next().onSingleTap(consumed, x, y);
1254 * Called just prior to a tap or press gesture being forwarded to the renderer.
1256 @SuppressWarnings("unused")
1258 private boolean filterTapOrPressEvent(int type, int x, int y) {
1259 if (type == GestureEventType.LONG_PRESS && offerLongPressToEmbedder()) {
1262 updateForTapOrPress(type, x, y);
1267 public void sendDoubleTapForTest(long timeMs, int x, int y) {
1268 if (mNativeContentViewCore == 0) return;
1269 nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1273 public void flingForTest(long timeMs, int x, int y, int velocityX, int velocityY) {
1274 if (mNativeContentViewCore == 0) return;
1275 nativeFlingCancel(mNativeContentViewCore, timeMs);
1276 nativeScrollBegin(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1277 nativeFlingStart(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1281 * Cancel any fling gestures active.
1282 * @param timeMs Current time (in milliseconds).
1284 public void cancelFling(long timeMs) {
1285 if (mNativeContentViewCore == 0) return;
1286 nativeFlingCancel(mNativeContentViewCore, timeMs);
1290 * Add a listener that gets alerted on gesture state changes.
1291 * @param listener Listener to add.
1293 public void addGestureStateListener(GestureStateListener listener) {
1294 mGestureStateListeners.addObserver(listener);
1298 * Removes a listener that was added to watch for gesture state changes.
1299 * @param listener Listener to remove.
1301 public void removeGestureStateListener(GestureStateListener listener) {
1302 mGestureStateListeners.removeObserver(listener);
1305 void updateGestureStateListener(int gestureType) {
1306 for (mGestureStateListenersIterator.rewind();
1307 mGestureStateListenersIterator.hasNext();) {
1308 GestureStateListener listener = mGestureStateListenersIterator.next();
1309 switch (gestureType) {
1310 case GestureEventType.PINCH_BEGIN:
1311 listener.onPinchStarted();
1313 case GestureEventType.PINCH_END:
1314 listener.onPinchEnded();
1316 case GestureEventType.FLING_END:
1317 listener.onFlingEndGesture(
1318 computeVerticalScrollOffset(),
1319 computeVerticalScrollExtent());
1321 case GestureEventType.FLING_CANCEL:
1322 listener.onFlingCancelGesture();
1324 case GestureEventType.SCROLL_START:
1325 listener.onScrollStarted(
1326 computeVerticalScrollOffset(),
1327 computeVerticalScrollExtent());
1329 case GestureEventType.SCROLL_END:
1330 listener.onScrollEnded(
1331 computeVerticalScrollOffset(),
1332 computeVerticalScrollExtent());
1341 * Inserts the provided markup sandboxed into the frame.
1343 public void setupTransitionView(String markup) {
1344 if (mWebContents != null) mWebContents.setupTransitionView(markup);
1348 * Hides transition elements specified by the selector, and activates any
1349 * exiting-transition stylesheets.
1351 public void beginExitTransition(String cssSelector) {
1352 if (mWebContents != null) mWebContents.beginExitTransition(cssSelector);
1356 * Requests the renderer insert a link to the specified stylesheet in the
1357 * main frame's document.
1359 public void addStyleSheetByURL(String url) {
1360 assert mWebContents != null;
1361 mWebContents.addStyleSheetByURL(url);
1365 * Injects the passed Javascript code in the current page and evaluates it.
1366 * If a result is required, pass in a callback.
1367 * Used in automation tests.
1369 * @param script The Javascript to execute.
1370 * @param callback The callback to be fired off when a result is ready. The script's
1371 * result will be json encoded and passed as the parameter, and the call
1372 * will be made on the main thread.
1373 * If no result is required, pass null.
1375 public void evaluateJavaScript(String script, JavaScriptCallback callback) {
1376 if (mWebContents != null) mWebContents.evaluateJavaScript(script, callback, false);
1380 * Injects the passed Javascript code in the current page and evaluates it.
1381 * If there is no page existing, a new one will be created.
1383 * @param script The Javascript to execute.
1385 public void evaluateJavaScriptEvenIfNotYetNavigated(String script) {
1386 if (mWebContents != null) mWebContents.evaluateJavaScript(script, null, true);
1390 * Post a message to a frame.
1391 * TODO(sgurun) also add support for transferring a message channel port.
1393 * @param frameName The name of the frame. If the name is null the message is posted
1394 * to the main frame.
1395 * @param message The message
1396 * @param sourceOrigin The source origin
1397 * @param targetOrigin The target origin
1399 public void postMessageToFrame(String frameName, String message,
1400 String sourceOrigin, String targetOrigin) {
1401 if (mNativeContentViewCore == 0) return;
1402 nativePostMessageToFrame(mNativeContentViewCore, frameName, message, sourceOrigin,
1407 * To be called when the ContentView is shown.
1409 public void onShow() {
1410 if (mWebContents == null) return;
1411 mWebContents.onShow();
1412 setAccessibilityState(mAccessibilityManager.isEnabled());
1416 * @return The ID of the renderer process that backs this tab or
1417 * {@link #INVALID_RENDER_PROCESS_PID} if there is none.
1419 public int getCurrentRenderProcessId() {
1420 return nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1424 * To be called when the ContentView is hidden.
1426 public void onHide() {
1427 assert mWebContents != null;
1429 setInjectedAccessibility(false);
1430 mWebContents.onHide();
1434 * Return the ContentSettings object used to retrieve the settings for this
1435 * ContentViewCore. For modifications, ChromeNativePreferences is to be used.
1436 * @return A ContentSettings object that can be used to retrieve this
1437 * ContentViewCore's settings.
1439 public ContentSettings getContentSettings() {
1440 return mContentSettings;
1443 private void hidePopups() {
1444 mUnselectAllOnActionModeDismiss = true;
1445 hideSelectActionBar();
1451 public void hideSelectActionBar() {
1452 if (mActionMode != null) {
1453 mActionMode.finish();
1458 public boolean isSelectActionBarShowing() {
1459 return mActionMode != null;
1462 private void resetGestureDetection() {
1463 if (mNativeContentViewCore == 0) return;
1464 nativeResetGestureDetection(mNativeContentViewCore);
1468 * @see View#onAttachedToWindow()
1470 @SuppressWarnings("javadoc")
1471 public void onAttachedToWindow() {
1472 setAccessibilityState(mAccessibilityManager.isEnabled());
1474 ScreenOrientationListener.getInstance().addObserver(this, mContext);
1475 GamepadList.onAttachedToWindow(mContext);
1479 * @see View#onDetachedFromWindow()
1481 @SuppressWarnings("javadoc")
1482 @SuppressLint("MissingSuperCall")
1483 public void onDetachedFromWindow() {
1484 setInjectedAccessibility(false);
1486 mZoomControlsDelegate.dismissZoomPicker();
1487 unregisterAccessibilityContentObserver();
1489 ScreenOrientationListener.getInstance().removeObserver(this);
1490 GamepadList.onDetachedFromWindow();
1494 * @see View#onVisibilityChanged(android.view.View, int)
1496 public void onVisibilityChanged(View changedView, int visibility) {
1497 if (visibility != View.VISIBLE) {
1498 mZoomControlsDelegate.dismissZoomPicker();
1503 * @see View#onCreateInputConnection(EditorInfo)
1505 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1506 if (!mImeAdapter.hasTextInputType()) {
1507 // Although onCheckIsTextEditor will return false in this case, the EditorInfo
1508 // is still used by the InputMethodService. Need to make sure the IME doesn't
1509 // enter fullscreen mode.
1510 outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
1512 mInputConnection = mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter,
1513 mEditable, outAttrs);
1514 return mInputConnection;
1518 public AdapterInputConnection getAdapterInputConnectionForTest() {
1519 return mInputConnection;
1523 public Editable getEditableForTest() {
1528 * @see View#onCheckIsTextEditor()
1530 public boolean onCheckIsTextEditor() {
1531 return mImeAdapter.hasTextInputType();
1535 * @see View#onConfigurationChanged(Configuration)
1537 @SuppressWarnings("javadoc")
1538 public void onConfigurationChanged(Configuration newConfig) {
1541 if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
1542 if (mNativeContentViewCore != 0) {
1543 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
1544 ImeAdapter.getTextInputTypeNone(), 0 /* no flags */);
1546 mInputMethodManagerWrapper.restartInput(mContainerView);
1548 mContainerViewInternals.super_onConfigurationChanged(newConfig);
1550 // To request layout has side effect, but it seems OK as it only happen in
1551 // onConfigurationChange and layout has to be changed in most case.
1552 mContainerView.requestLayout();
1557 * @see View#onSizeChanged(int, int, int, int)
1559 @SuppressWarnings("javadoc")
1560 public void onSizeChanged(int wPix, int hPix, int owPix, int ohPix) {
1561 if (getViewportWidthPix() == wPix && getViewportHeightPix() == hPix) return;
1563 mViewportWidthPix = wPix;
1564 mViewportHeightPix = hPix;
1565 if (mNativeContentViewCore != 0) {
1566 nativeWasResized(mNativeContentViewCore);
1569 updateAfterSizeChanged();
1573 * Called when the underlying surface the compositor draws to changes size.
1574 * This may be larger than the viewport size.
1576 public void onPhysicalBackingSizeChanged(int wPix, int hPix) {
1577 if (mPhysicalBackingWidthPix == wPix && mPhysicalBackingHeightPix == hPix) return;
1579 mPhysicalBackingWidthPix = wPix;
1580 mPhysicalBackingHeightPix = hPix;
1582 if (mNativeContentViewCore != 0) {
1583 nativeWasResized(mNativeContentViewCore);
1588 * Called when the amount the surface is overdrawing off the bottom has changed.
1589 * @param overdrawHeightPix The overdraw height.
1591 public void onOverdrawBottomHeightChanged(int overdrawHeightPix) {
1592 if (mOverdrawBottomHeightPix == overdrawHeightPix) return;
1594 mOverdrawBottomHeightPix = overdrawHeightPix;
1596 if (mNativeContentViewCore != 0) {
1597 nativeWasResized(mNativeContentViewCore);
1601 private void updateAfterSizeChanged() {
1602 mPopupZoomer.hide(false);
1604 // Execute a delayed form focus operation because the OSK was brought
1606 if (!mFocusPreOSKViewportRect.isEmpty()) {
1607 Rect rect = new Rect();
1608 getContainerView().getWindowVisibleDisplayFrame(rect);
1609 if (!rect.equals(mFocusPreOSKViewportRect)) {
1610 // Only assume the OSK triggered the onSizeChanged if width was preserved.
1611 if (rect.width() == mFocusPreOSKViewportRect.width()) {
1612 scrollFocusedEditableNodeIntoView();
1614 cancelRequestToScrollFocusedEditableNodeIntoView();
1619 private void cancelRequestToScrollFocusedEditableNodeIntoView() {
1620 // Zero-ing the rect will prevent |updateAfterSizeChanged()| from
1621 // issuing the delayed form focus event.
1622 mFocusPreOSKViewportRect.setEmpty();
1625 private void scrollFocusedEditableNodeIntoView() {
1626 assert mWebContents != null;
1627 mWebContents.scrollFocusedEditableNodeIntoView();
1631 * Selects the word around the caret, if any.
1632 * The caller can check if selection actually occurred by listening to OnSelectionChanged.
1634 public void selectWordAroundCaret() {
1635 assert mWebContents != null;
1636 mWebContents.selectWordAroundCaret();
1640 * @see View#onWindowFocusChanged(boolean)
1642 public void onWindowFocusChanged(boolean hasWindowFocus) {
1643 if (!hasWindowFocus) resetGestureDetection();
1646 public void onFocusChanged(boolean gainFocus) {
1649 cancelRequestToScrollFocusedEditableNodeIntoView();
1653 if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1657 * @see View#onKeyUp(int, KeyEvent)
1659 public boolean onKeyUp(int keyCode, KeyEvent event) {
1660 if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1661 mPopupZoomer.hide(true);
1664 return mContainerViewInternals.super_onKeyUp(keyCode, event);
1668 * @see View#dispatchKeyEventPreIme(KeyEvent)
1670 public boolean dispatchKeyEventPreIme(KeyEvent event) {
1673 return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1680 * @see View#dispatchKeyEvent(KeyEvent)
1682 public boolean dispatchKeyEvent(KeyEvent event) {
1683 if (GamepadList.dispatchKeyEvent(event)) return true;
1684 if (getContentViewClient().shouldOverrideKeyEvent(event)) {
1685 return mContainerViewInternals.super_dispatchKeyEvent(event);
1688 if (mImeAdapter.dispatchKeyEvent(event)) return true;
1690 return mContainerViewInternals.super_dispatchKeyEvent(event);
1694 * @see View#onHoverEvent(MotionEvent)
1695 * Mouse move events are sent on hover enter, hover move and hover exit.
1696 * They are sent on hover exit because sometimes it acts as both a hover
1697 * move and hover exit.
1699 public boolean onHoverEvent(MotionEvent event) {
1700 TraceEvent.begin("onHoverEvent");
1701 MotionEvent offset = createOffsetMotionEvent(event);
1703 if (mBrowserAccessibilityManager != null) {
1704 return mBrowserAccessibilityManager.onHoverEvent(offset);
1707 // Work around Android bug where the x, y coordinates of a hover exit
1708 // event are incorrect when touch exploration is on.
1709 if (mTouchExplorationEnabled && offset.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
1713 mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1714 if (mNativeContentViewCore != 0) {
1715 nativeSendMouseMoveEvent(mNativeContentViewCore, offset.getEventTime(),
1716 offset.getX(), offset.getY());
1721 TraceEvent.end("onHoverEvent");
1726 * @see View#onGenericMotionEvent(MotionEvent)
1728 public boolean onGenericMotionEvent(MotionEvent event) {
1729 if (GamepadList.onGenericMotionEvent(event)) return true;
1730 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1731 switch (event.getAction()) {
1732 case MotionEvent.ACTION_SCROLL:
1733 if (mNativeContentViewCore == 0) return false;
1735 nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
1736 event.getX(), event.getY(),
1737 event.getAxisValue(MotionEvent.AXIS_VSCROLL));
1739 mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1740 // Send a delayed onMouseMove event so that we end
1741 // up hovering over the right position after the scroll.
1742 final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event);
1743 mFakeMouseMoveRunnable = new Runnable() {
1746 onHoverEvent(eventFakeMouseMove);
1747 eventFakeMouseMove.recycle();
1750 mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1754 return mContainerViewInternals.super_onGenericMotionEvent(event);
1758 * Sets the current amount to offset incoming touch events by. This is used to handle content
1759 * moving and not lining up properly with the android input system.
1760 * @param dx The X offset in pixels to shift touch events.
1761 * @param dy The Y offset in pixels to shift touch events.
1763 public void setCurrentMotionEventOffsets(float dx, float dy) {
1764 mCurrentTouchOffsetX = dx;
1765 mCurrentTouchOffsetY = dy;
1768 private MotionEvent createOffsetMotionEvent(MotionEvent src) {
1769 MotionEvent dst = MotionEvent.obtain(src);
1770 dst.offsetLocation(mCurrentTouchOffsetX, mCurrentTouchOffsetY);
1775 * @see View#scrollBy(int, int)
1776 * Currently the ContentView scrolling happens in the native side. In
1777 * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
1778 * are overridden, so that View's mScrollX and mScrollY will be unchanged at
1779 * (0, 0). This is critical for drawing ContentView correctly.
1781 public void scrollBy(int xPix, int yPix) {
1782 if (mNativeContentViewCore != 0) {
1783 nativeScrollBy(mNativeContentViewCore,
1784 SystemClock.uptimeMillis(), 0, 0, xPix, yPix);
1789 * @see View#scrollTo(int, int)
1791 public void scrollTo(int xPix, int yPix) {
1792 if (mNativeContentViewCore == 0) return;
1793 final float xCurrentPix = mRenderCoordinates.getScrollXPix();
1794 final float yCurrentPix = mRenderCoordinates.getScrollYPix();
1795 final float dxPix = xPix - xCurrentPix;
1796 final float dyPix = yPix - yCurrentPix;
1797 if (dxPix != 0 || dyPix != 0) {
1798 long time = SystemClock.uptimeMillis();
1799 nativeScrollBegin(mNativeContentViewCore, time,
1800 xCurrentPix, yCurrentPix, -dxPix, -dyPix);
1801 nativeScrollBy(mNativeContentViewCore,
1802 time, xCurrentPix, yCurrentPix, dxPix, dyPix);
1803 nativeScrollEnd(mNativeContentViewCore, time);
1807 // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1809 public int getNativeScrollXForTest() {
1810 return mRenderCoordinates.getScrollXPixInt();
1813 // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1815 public int getNativeScrollYForTest() {
1816 return mRenderCoordinates.getScrollYPixInt();
1820 * @see View#computeHorizontalScrollExtent()
1822 @SuppressWarnings("javadoc")
1823 public int computeHorizontalScrollExtent() {
1824 return mRenderCoordinates.getLastFrameViewportWidthPixInt();
1828 * @see View#computeHorizontalScrollOffset()
1830 @SuppressWarnings("javadoc")
1831 public int computeHorizontalScrollOffset() {
1832 return mRenderCoordinates.getScrollXPixInt();
1836 * @see View#computeHorizontalScrollRange()
1838 @SuppressWarnings("javadoc")
1839 public int computeHorizontalScrollRange() {
1840 return mRenderCoordinates.getContentWidthPixInt();
1844 * @see View#computeVerticalScrollExtent()
1846 @SuppressWarnings("javadoc")
1847 public int computeVerticalScrollExtent() {
1848 return mRenderCoordinates.getLastFrameViewportHeightPixInt();
1852 * @see View#computeVerticalScrollOffset()
1854 @SuppressWarnings("javadoc")
1855 public int computeVerticalScrollOffset() {
1856 return mRenderCoordinates.getScrollYPixInt();
1860 * @see View#computeVerticalScrollRange()
1862 @SuppressWarnings("javadoc")
1863 public int computeVerticalScrollRange() {
1864 return mRenderCoordinates.getContentHeightPixInt();
1867 // End FrameLayout overrides.
1870 * @see View#awakenScrollBars(int, boolean)
1872 @SuppressWarnings("javadoc")
1873 public boolean awakenScrollBars(int startDelay, boolean invalidate) {
1874 // For the default implementation of ContentView which draws the scrollBars on the native
1875 // side, calling this function may get us into a bad state where we keep drawing the
1876 // scrollBars, so disable it by always returning false.
1877 if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
1880 return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
1884 private void updateForTapOrPress(int type, float xPix, float yPix) {
1885 if (type != GestureEventType.SINGLE_TAP_CONFIRMED
1886 && type != GestureEventType.SINGLE_TAP_UP
1887 && type != GestureEventType.LONG_PRESS
1888 && type != GestureEventType.LONG_TAP) {
1892 if (mContainerView.isFocusable() && mContainerView.isFocusableInTouchMode()
1893 && !mContainerView.isFocused()) {
1894 mContainerView.requestFocus();
1897 if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
1899 mLastTapX = (int) xPix;
1900 mLastTapY = (int) yPix;
1904 * @return The x coordinate for the last point that a tap or press gesture was initiated from.
1906 public int getLastTapX() {
1911 * @return The y coordinate for the last point that a tap or press gesture was initiated from.
1913 public int getLastTapY() {
1917 public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
1918 mZoomControlsDelegate = zoomControlsDelegate;
1921 public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
1922 if (mNativeContentViewCore == 0) return;
1923 nativeSetMultiTouchZoomSupportEnabled(mNativeContentViewCore, supportsMultiTouchZoom);
1926 public void updateDoubleTapSupport(boolean supportsDoubleTap) {
1927 if (mNativeContentViewCore == 0) return;
1928 nativeSetDoubleTapSupportEnabled(mNativeContentViewCore, supportsDoubleTap);
1931 public void selectPopupMenuItems(int[] indices) {
1932 if (mNativeContentViewCore != 0) {
1933 nativeSelectPopupMenuItems(mNativeContentViewCore, indices);
1935 mSelectPopup = null;
1939 * Send the screen orientation value to the renderer.
1942 void sendOrientationChangeEvent(int orientation) {
1943 if (mNativeContentViewCore == 0) return;
1945 nativeSendOrientationChangeEvent(mNativeContentViewCore, orientation);
1949 * Register the delegate to be used when content can not be handled by
1950 * the rendering engine, and should be downloaded instead. This will replace
1951 * the current delegate, if any.
1952 * @param delegate An implementation of ContentViewDownloadDelegate.
1954 public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
1955 mDownloadDelegate = delegate;
1958 // Called by DownloadController.
1959 ContentViewDownloadDelegate getDownloadDelegate() {
1960 return mDownloadDelegate;
1963 private void showSelectActionBar() {
1964 if (mActionMode != null) {
1965 mActionMode.invalidate();
1969 // Start a new action mode with a SelectActionModeCallback.
1970 SelectActionModeCallback.ActionHandler actionHandler =
1971 new SelectActionModeCallback.ActionHandler() {
1973 public void selectAll() {
1974 mImeAdapter.selectAll();
1983 public void copy() {
1988 public void paste() {
1989 mImeAdapter.paste();
1993 public void share() {
1994 final String query = getSelectedText();
1995 if (TextUtils.isEmpty(query)) return;
1997 Intent send = new Intent(Intent.ACTION_SEND);
1998 send.setType("text/plain");
1999 send.putExtra(Intent.EXTRA_TEXT, query);
2001 Intent i = Intent.createChooser(send, getContext().getString(
2002 R.string.actionbar_share));
2003 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2004 getContext().startActivity(i);
2005 } catch (android.content.ActivityNotFoundException ex) {
2006 // If no app handles it, do nothing.
2011 public void search() {
2012 final String query = getSelectedText();
2013 if (TextUtils.isEmpty(query)) return;
2015 // See if ContentViewClient wants to override
2016 if (getContentViewClient().doesPerformWebSearch()) {
2017 getContentViewClient().performWebSearch(query);
2021 Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
2022 i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2023 i.putExtra(SearchManager.QUERY, query);
2024 i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
2025 if (!(getContext() instanceof Activity)) {
2026 i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2029 getContext().startActivity(i);
2030 } catch (android.content.ActivityNotFoundException ex) {
2031 // If no app handles it, do nothing.
2036 public boolean isSelectionPassword() {
2037 return mImeAdapter.isSelectionPassword();
2041 public boolean isSelectionEditable() {
2042 return mFocusedNodeEditable;
2046 public void onDestroyActionMode() {
2048 if (mUnselectAllOnActionModeDismiss) {
2050 if (isSelectionEditable()) {
2051 int selectionEnd = Selection.getSelectionEnd(mEditable);
2052 mInputConnection.setSelection(selectionEnd, selectionEnd);
2054 mImeAdapter.unselect();
2057 getContentViewClient().onContextualActionBarHidden();
2061 public boolean isShareAvailable() {
2062 Intent intent = new Intent(Intent.ACTION_SEND);
2063 intent.setType("text/plain");
2064 return getContext().getPackageManager().queryIntentActivities(intent,
2065 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2069 public boolean isWebSearchAvailable() {
2070 if (getContentViewClient().doesPerformWebSearch()) return true;
2071 Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
2072 intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2073 return getContext().getPackageManager().queryIntentActivities(intent,
2074 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2078 // On ICS, startActionMode throws an NPE when getParent() is null.
2079 if (mContainerView.getParent() != null) {
2080 assert mWebContents != null;
2081 mActionMode = mContainerView.startActionMode(
2082 getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler,
2083 mWebContents.isIncognito()));
2085 mUnselectAllOnActionModeDismiss = true;
2086 if (mActionMode == null) {
2087 // There is no ActionMode, so remove the selection.
2088 mImeAdapter.unselect();
2090 getContentViewClient().onContextualActionBarShown();
2095 * Clears the current text selection.
2097 public void clearSelection() {
2098 mImeAdapter.unselect();
2101 private void hidePastePopup() {
2102 if (mPastePopupMenu == null) return;
2103 mPastePopupMenu.hide();
2107 private void onSelectionEvent(int eventType, float posXDip, float posYDip) {
2108 switch (eventType) {
2109 case SelectionEventType.SELECTION_SHOWN:
2110 mHasSelection = true;
2111 // TODO(cjhopman): Remove this when there is a better signal that long press caused
2112 // a selection. See http://crbug.com/150151.
2113 mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2114 showSelectActionBar();
2117 case SelectionEventType.SELECTION_CLEARED:
2118 mHasSelection = false;
2119 mUnselectAllOnActionModeDismiss = false;
2120 hideSelectActionBar();
2123 case SelectionEventType.SELECTION_DRAG_STARTED:
2126 case SelectionEventType.SELECTION_DRAG_STOPPED:
2129 case SelectionEventType.INSERTION_SHOWN:
2130 mHasInsertion = true;
2133 case SelectionEventType.INSERTION_MOVED:
2134 if (mPastePopupMenu == null) break;
2135 if (!isScrollInProgress() && mPastePopupMenu.isShowing()) {
2136 showPastePopup((int) posXDip, (int) posYDip);
2142 case SelectionEventType.INSERTION_TAPPED:
2143 if (mWasPastePopupShowingOnInsertionDragStart)
2146 showPastePopup((int) posXDip, (int) posYDip);
2149 case SelectionEventType.INSERTION_CLEARED:
2150 mHasInsertion = false;
2154 case SelectionEventType.INSERTION_DRAG_STARTED:
2155 mWasPastePopupShowingOnInsertionDragStart =
2156 mPastePopupMenu != null && mPastePopupMenu.isShowing();
2161 assert false : "Invalid selection event type.";
2163 getContentViewClient().onSelectionEvent(eventType);
2166 public boolean getUseDesktopUserAgent() {
2167 if (mNativeContentViewCore != 0) {
2168 return nativeGetUseDesktopUserAgent(mNativeContentViewCore);
2174 * Set whether or not we're using a desktop user agent for the currently loaded page.
2175 * @param override If true, use a desktop user agent. Use a mobile one otherwise.
2176 * @param reloadOnChange Reload the page if the UA has changed.
2178 public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange) {
2179 if (mNativeContentViewCore != 0) {
2180 nativeSetUseDesktopUserAgent(mNativeContentViewCore, override, reloadOnChange);
2184 public void clearSslPreferences() {
2185 if (mNativeContentViewCore != 0) nativeClearSslPreferences(mNativeContentViewCore);
2188 private void hideTextHandles() {
2189 mHasSelection = false;
2190 mHasInsertion = false;
2191 if (mNativeContentViewCore != 0) nativeHideTextHandles(mNativeContentViewCore);
2195 * Shows the IME if the focused widget could accept text input.
2197 public void showImeIfNeeded() {
2198 assert mWebContents != null;
2199 mWebContents.showImeIfNeeded();
2203 * Hides the IME if the containerView is the active view for IME.
2205 public void hideImeIfNeeded() {
2206 // Hide input method window from the current view synchronously
2207 // because ImeAdapter does so asynchronouly with a delay, and
2208 // by the time when ImeAdapter dismisses the input, the
2209 // containerView may have lost focus.
2210 // We cannot trust ContentViewClient#onImeStateChangeRequested to
2211 // hide the input window because it has an empty default implementation.
2212 // So we need to explicitly hide the input method window here.
2213 if (mInputMethodManagerWrapper.isActive(mContainerView)) {
2214 mInputMethodManagerWrapper.hideSoftInputFromWindow(
2215 mContainerView.getWindowToken(), 0, null);
2217 getContentViewClient().onImeStateChangeRequested(false);
2220 @SuppressWarnings("unused")
2222 private void updateFrameInfo(
2223 float scrollOffsetX, float scrollOffsetY,
2224 float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
2225 float contentWidth, float contentHeight,
2226 float viewportWidth, float viewportHeight,
2227 float controlsOffsetYCss, float contentOffsetYCss,
2228 float overdrawBottomHeightCss) {
2229 TraceEvent.begin("ContentViewCore:updateFrameInfo");
2230 // Adjust contentWidth/Height to be always at least as big as
2231 // the actual viewport (as set by onSizeChanged).
2232 final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2233 contentWidth = Math.max(contentWidth,
2234 mViewportWidthPix / (deviceScale * pageScaleFactor));
2235 contentHeight = Math.max(contentHeight,
2236 mViewportHeightPix / (deviceScale * pageScaleFactor));
2237 final float contentOffsetYPix = mRenderCoordinates.fromDipToPix(contentOffsetYCss);
2239 final boolean contentSizeChanged =
2240 contentWidth != mRenderCoordinates.getContentWidthCss()
2241 || contentHeight != mRenderCoordinates.getContentHeightCss();
2242 final boolean scaleLimitsChanged =
2243 minPageScaleFactor != mRenderCoordinates.getMinPageScaleFactor()
2244 || maxPageScaleFactor != mRenderCoordinates.getMaxPageScaleFactor();
2245 final boolean pageScaleChanged =
2246 pageScaleFactor != mRenderCoordinates.getPageScaleFactor();
2247 final boolean scrollChanged =
2249 || scrollOffsetX != mRenderCoordinates.getScrollX()
2250 || scrollOffsetY != mRenderCoordinates.getScrollY();
2251 final boolean contentOffsetChanged =
2252 contentOffsetYPix != mRenderCoordinates.getContentOffsetYPix();
2254 final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
2255 final boolean needUpdateZoomControls = scaleLimitsChanged || scrollChanged;
2257 if (needHidePopupZoomer) mPopupZoomer.hide(true);
2259 if (scrollChanged) {
2260 mContainerViewInternals.onScrollChanged(
2261 (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetX),
2262 (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetY),
2263 (int) mRenderCoordinates.getScrollXPix(),
2264 (int) mRenderCoordinates.getScrollYPix());
2267 mRenderCoordinates.updateFrameInfo(
2268 scrollOffsetX, scrollOffsetY,
2269 contentWidth, contentHeight,
2270 viewportWidth, viewportHeight,
2271 pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
2274 if (scrollChanged || contentOffsetChanged) {
2275 for (mGestureStateListenersIterator.rewind();
2276 mGestureStateListenersIterator.hasNext();) {
2277 mGestureStateListenersIterator.next().onScrollOffsetOrExtentChanged(
2278 computeVerticalScrollOffset(),
2279 computeVerticalScrollExtent());
2283 if (needUpdateZoomControls) mZoomControlsDelegate.updateZoomControls();
2285 // Update offsets for fullscreen.
2286 final float controlsOffsetPix = controlsOffsetYCss * deviceScale;
2287 final float overdrawBottomHeightPix = overdrawBottomHeightCss * deviceScale;
2288 getContentViewClient().onOffsetsForFullscreenChanged(
2289 controlsOffsetPix, contentOffsetYPix, overdrawBottomHeightPix);
2291 if (mBrowserAccessibilityManager != null) {
2292 mBrowserAccessibilityManager.notifyFrameInfoInitialized();
2294 TraceEvent.end("ContentViewCore:updateFrameInfo");
2298 private void updateImeAdapter(long nativeImeAdapterAndroid, int textInputType,
2299 int textInputFlags, String text, int selectionStart, int selectionEnd,
2300 int compositionStart, int compositionEnd, boolean showImeIfNeeded,
2301 boolean isNonImeChange) {
2303 mFocusedNodeEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
2304 if (!mFocusedNodeEditable) hidePastePopup();
2306 mImeAdapter.updateKeyboardVisibility(
2307 nativeImeAdapterAndroid, textInputType, textInputFlags, showImeIfNeeded);
2309 if (mInputConnection != null) {
2310 mInputConnection.updateState(text, selectionStart, selectionEnd, compositionStart,
2311 compositionEnd, isNonImeChange);
2314 if (mActionMode != null) mActionMode.invalidate();
2318 @SuppressWarnings("unused")
2320 private void setTitle(String title) {
2321 getContentViewClient().onUpdateTitle(title);
2325 * Called (from native) when the <select> popup needs to be shown.
2326 * @param items Items to show.
2327 * @param enabled POPUP_ITEM_TYPEs for items.
2328 * @param multiple Whether the popup menu should support multi-select.
2329 * @param selectedIndices Indices of selected items.
2331 @SuppressWarnings("unused")
2333 private void showSelectPopup(Rect bounds, String[] items, int[] enabled, boolean multiple,
2334 int[] selectedIndices) {
2335 if (mContainerView.getParent() == null || mContainerView.getVisibility() != View.VISIBLE) {
2336 selectPopupMenuItems(null);
2340 assert items.length == enabled.length;
2341 List<SelectPopupItem> popupItems = new ArrayList<SelectPopupItem>();
2342 for (int i = 0; i < items.length; i++) {
2343 popupItems.add(new SelectPopupItem(items[i], enabled[i]));
2346 if (DeviceFormFactor.isTablet(mContext) && !multiple) {
2347 mSelectPopup = new SelectPopupDropdown(this, popupItems, bounds, selectedIndices);
2349 mSelectPopup = new SelectPopupDialog(this, popupItems, multiple, selectedIndices);
2351 mSelectPopup.show();
2355 * Called when the <select> popup needs to be hidden.
2358 private void hideSelectPopup() {
2359 if (mSelectPopup != null) mSelectPopup.hide();
2363 * @return The visible select popup being shown.
2365 public SelectPopup getSelectPopupForTest() {
2366 return mSelectPopup;
2369 @SuppressWarnings("unused")
2371 private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
2372 mPopupZoomer.setBitmap(zoomedBitmap);
2373 mPopupZoomer.show(targetRect);
2376 @SuppressWarnings("unused")
2378 private TouchEventSynthesizer createTouchEventSynthesizer() {
2379 return new TouchEventSynthesizer(this);
2382 @SuppressWarnings("unused")
2384 private PopupTouchHandleDrawable createPopupTouchHandleDrawable() {
2385 if (mTouchHandleDelegate == null) {
2386 mTouchHandleDelegate = new PopupTouchHandleDrawableDelegate() {
2387 public View getParent() {
2388 return getContainerView();
2391 public PositionObserver getParentPositionObserver() {
2392 return mPositionObserver;
2395 public boolean onTouchHandleEvent(MotionEvent event) {
2396 final boolean isTouchHandleEvent = true;
2397 return onTouchEventImpl(event, isTouchHandleEvent);
2401 return new PopupTouchHandleDrawable(mTouchHandleDelegate);
2404 @SuppressWarnings("unused")
2406 private void onSelectionChanged(String text) {
2407 mLastSelectedText = text;
2408 getContentViewClient().onSelectionChanged(text);
2411 @SuppressWarnings("unused")
2413 private void showPastePopupWithFeedback(int xDip, int yDip) {
2414 // TODO(jdduke): Remove this when there is a better signal that long press caused
2415 // showing of the paste popup. See http://crbug.com/150151.
2416 if (showPastePopup(xDip, yDip)) {
2417 mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2421 private boolean showPastePopup(int xDip, int yDip) {
2422 if (!mHasInsertion || !canPaste()) return false;
2423 final float contentOffsetYPix = mRenderCoordinates.getContentOffsetYPix();
2424 getPastePopup().showAt(
2425 (int) mRenderCoordinates.fromDipToPix(xDip),
2426 (int) (mRenderCoordinates.fromDipToPix(yDip) + contentOffsetYPix));
2430 private PastePopupMenu getPastePopup() {
2431 if (mPastePopupMenu == null) {
2432 mPastePopupMenu = new PastePopupMenu(getContainerView(),
2433 new PastePopupMenuDelegate() {
2434 public void paste() {
2435 mImeAdapter.paste();
2440 return mPastePopupMenu;
2443 private boolean canPaste() {
2444 if (!mFocusedNodeEditable) return false;
2445 return ((ClipboardManager) mContext.getSystemService(
2446 Context.CLIPBOARD_SERVICE)).hasPrimaryClip();
2449 @SuppressWarnings("unused")
2451 private void onRenderProcessChange() {
2456 * Attaches the native ImeAdapter object to the java ImeAdapter to allow communication via JNI.
2458 public void attachImeAdapter() {
2459 if (mImeAdapter != null && mNativeContentViewCore != 0) {
2460 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2465 * @see View#hasFocus()
2468 public boolean hasFocus() {
2469 return mContainerView.hasFocus();
2473 * Checks whether the ContentViewCore can be zoomed in.
2475 * @return True if the ContentViewCore can be zoomed in.
2477 // This method uses the term 'zoom' for legacy reasons, but relates
2478 // to what chrome calls the 'page scale factor'.
2479 public boolean canZoomIn() {
2480 final float zoomInExtent = mRenderCoordinates.getMaxPageScaleFactor()
2481 - mRenderCoordinates.getPageScaleFactor();
2482 return zoomInExtent > ZOOM_CONTROLS_EPSILON;
2486 * Checks whether the ContentViewCore can be zoomed out.
2488 * @return True if the ContentViewCore can be zoomed out.
2490 // This method uses the term 'zoom' for legacy reasons, but relates
2491 // to what chrome calls the 'page scale factor'.
2492 public boolean canZoomOut() {
2493 final float zoomOutExtent = mRenderCoordinates.getPageScaleFactor()
2494 - mRenderCoordinates.getMinPageScaleFactor();
2495 return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
2499 * Zooms in the ContentViewCore by 25% (or less if that would result in
2500 * zooming in more than possible).
2502 * @return True if there was a zoom change, false otherwise.
2504 // This method uses the term 'zoom' for legacy reasons, but relates
2505 // to what chrome calls the 'page scale factor'.
2506 public boolean zoomIn() {
2510 return pinchByDelta(1.25f);
2514 * Zooms out the ContentViewCore by 20% (or less if that would result in
2515 * zooming out more than possible).
2517 * @return True if there was a zoom change, false otherwise.
2519 // This method uses the term 'zoom' for legacy reasons, but relates
2520 // to what chrome calls the 'page scale factor'.
2521 public boolean zoomOut() {
2522 if (!canZoomOut()) {
2525 return pinchByDelta(0.8f);
2529 * Resets the zoom factor of the ContentViewCore.
2531 * @return True if there was a zoom change, false otherwise.
2533 // This method uses the term 'zoom' for legacy reasons, but relates
2534 // to what chrome calls the 'page scale factor'.
2535 public boolean zoomReset() {
2536 // The page scale factor is initialized to mNativeMinimumScale when
2537 // the page finishes loading. Thus sets it back to mNativeMinimumScale.
2538 if (!canZoomOut()) return false;
2539 return pinchByDelta(
2540 mRenderCoordinates.getMinPageScaleFactor()
2541 / mRenderCoordinates.getPageScaleFactor());
2545 * Simulate a pinch zoom gesture.
2547 * @param delta the factor by which the current page scale should be multiplied by.
2548 * @return whether the gesture was sent.
2550 public boolean pinchByDelta(float delta) {
2551 if (mNativeContentViewCore == 0) return false;
2553 long timeMs = SystemClock.uptimeMillis();
2554 int xPix = getViewportWidthPix() / 2;
2555 int yPix = getViewportHeightPix() / 2;
2557 nativePinchBegin(mNativeContentViewCore, timeMs, xPix, yPix);
2558 nativePinchBy(mNativeContentViewCore, timeMs, xPix, yPix, delta);
2559 nativePinchEnd(mNativeContentViewCore, timeMs);
2565 * Invokes the graphical zoom picker widget for this ContentView.
2567 public void invokeZoomPicker() {
2568 mZoomControlsDelegate.invokeZoomPicker();
2572 * Enables or disables inspection of JavaScript objects added via
2573 * {@link #addJavascriptInterface(Object, String)} by means of Object.keys() method and
2574 * "for .. in" loop. Being able to inspect JavaScript objects is useful
2575 * when debugging hybrid Android apps, but can't be enabled for legacy applications due
2576 * to compatibility risks.
2578 * @param allow Whether to allow JavaScript objects inspection.
2580 public void setAllowJavascriptInterfacesInspection(boolean allow) {
2581 nativeSetAllowJavascriptInterfacesInspection(mNativeContentViewCore, allow);
2585 * Returns JavaScript interface objects previously injected via
2586 * {@link #addJavascriptInterface(Object, String)}.
2588 * @return the mapping of names to interface objects and corresponding annotation classes
2590 public Map<String, Pair<Object, Class>> getJavascriptInterfaces() {
2591 return mJavaScriptInterfaces;
2595 * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
2596 * and automatically pass in {@link JavascriptInterface} as the required annotation.
2598 * @param object The Java object to inject into the ContentViewCore's JavaScript context. Null
2599 * values are ignored.
2600 * @param name The name used to expose the instance in JavaScript.
2602 public void addJavascriptInterface(Object object, String name) {
2603 addPossiblyUnsafeJavascriptInterface(object, name, JavascriptInterface.class);
2607 * This method injects the supplied Java object into the ContentViewCore.
2608 * The object is injected into the JavaScript context of the main frame,
2609 * using the supplied name. This allows the Java object to be accessed from
2610 * JavaScript. Note that that injected objects will not appear in
2611 * JavaScript until the page is next (re)loaded. For example:
2612 * <pre> view.addJavascriptInterface(new Object(), "injectedObject");
2613 * view.loadData("<!DOCTYPE html><title></title>", "text/html", null);
2614 * view.loadUrl("javascript:alert(injectedObject.toString())");</pre>
2615 * <p><strong>IMPORTANT:</strong>
2617 * <li> addJavascriptInterface() can be used to allow JavaScript to control
2618 * the host application. This is a powerful feature, but also presents a
2619 * security risk. Use of this method in a ContentViewCore containing
2620 * untrusted content could allow an attacker to manipulate the host
2621 * application in unintended ways, executing Java code with the permissions
2622 * of the host application. Use extreme care when using this method in a
2623 * ContentViewCore which could contain untrusted content. Particular care
2624 * should be taken to avoid unintentional access to inherited methods, such
2625 * as {@link Object#getClass()}. To prevent access to inherited methods,
2626 * pass an annotation for {@code requiredAnnotation}. This will ensure
2627 * that only methods with {@code requiredAnnotation} are exposed to the
2628 * Javascript layer. {@code requiredAnnotation} will be passed to all
2629 * subsequently injected Java objects if any methods return an object. This
2630 * means the same restrictions (or lack thereof) will apply. Alternatively,
2631 * {@link #addJavascriptInterface(Object, String)} can be called, which
2632 * automatically uses the {@link JavascriptInterface} annotation.
2633 * <li> JavaScript interacts with Java objects on a private, background
2634 * thread of the ContentViewCore. Care is therefore required to maintain
2635 * thread safety.</li>
2638 * @param object The Java object to inject into the
2639 * ContentViewCore's JavaScript context. Null
2640 * values are ignored.
2641 * @param name The name used to expose the instance in
2643 * @param requiredAnnotation Restrict exposed methods to ones with this
2644 * annotation. If {@code null} all methods are
2648 public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
2649 Class<? extends Annotation> requiredAnnotation) {
2650 if (mNativeContentViewCore != 0 && object != null) {
2651 mJavaScriptInterfaces.put(name, new Pair<Object, Class>(object, requiredAnnotation));
2652 nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation);
2657 * Removes a previously added JavaScript interface with the given name.
2659 * @param name The name of the interface to remove.
2661 public void removeJavascriptInterface(String name) {
2662 mJavaScriptInterfaces.remove(name);
2663 if (mNativeContentViewCore != 0) {
2664 nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2669 * Return the current scale of the ContentView.
2670 * @return The current page scale factor.
2672 public float getScale() {
2673 return mRenderCoordinates.getPageScaleFactor();
2677 * If the view is ready to draw contents to the screen. In hardware mode,
2678 * the initialization of the surface texture may not occur until after the
2679 * view has been added to the layout. This method will return {@code true}
2680 * once the texture is actually ready.
2682 public boolean isReady() {
2683 assert mWebContents != null;
2684 return mWebContents.isReady();
2688 private void startContentIntent(String contentUrl) {
2689 getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2693 public void onAccessibilityStateChanged(boolean enabled) {
2694 setAccessibilityState(enabled);
2698 * Determines whether or not this ContentViewCore can handle this accessibility action.
2699 * @param action The action to perform.
2700 * @return Whether or not this action is supported.
2702 public boolean supportsAccessibilityAction(int action) {
2703 return mAccessibilityInjector.supportsAccessibilityAction(action);
2707 * Attempts to perform an accessibility action on the web content. If the accessibility action
2708 * cannot be processed, it returns {@code null}, allowing the caller to know to call the
2709 * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value.
2710 * Otherwise the return value from this method should be used.
2711 * @param action The action to perform.
2712 * @param arguments Optional action arguments.
2713 * @return Whether the action was performed or {@code null} if the call should be delegated to
2714 * the super {@link View} class.
2716 public boolean performAccessibilityAction(int action, Bundle arguments) {
2717 if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2718 return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2725 * Set the BrowserAccessibilityManager, used for native accessibility
2726 * (not script injection). This is only set when system accessibility
2728 * @param manager The new BrowserAccessibilityManager.
2730 public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
2731 mBrowserAccessibilityManager = manager;
2735 * Get the BrowserAccessibilityManager, used for native accessibility
2736 * (not script injection). This will return null when system accessibility
2738 * @return This view's BrowserAccessibilityManager.
2740 public BrowserAccessibilityManager getBrowserAccessibilityManager() {
2741 return mBrowserAccessibilityManager;
2745 * If native accessibility (not script injection) is enabled, and if this is
2746 * running on JellyBean or later, returns an AccessibilityNodeProvider that
2747 * implements native accessibility for this view. Returns null otherwise.
2748 * Lazily initializes native accessibility here if it's allowed.
2749 * @return The AccessibilityNodeProvider, if available, or null otherwise.
2751 public AccessibilityNodeProvider getAccessibilityNodeProvider() {
2752 if (mBrowserAccessibilityManager != null) {
2753 return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
2756 if (mNativeAccessibilityAllowed &&
2757 !mNativeAccessibilityEnabled &&
2758 mNativeContentViewCore != 0 &&
2759 Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
2760 mNativeAccessibilityEnabled = true;
2761 nativeSetAccessibilityEnabled(mNativeContentViewCore, true);
2768 * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
2770 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2771 // Note: this is only used by the script-injecting accessibility code.
2772 mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
2776 * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
2778 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
2779 // Note: this is only used by the script-injecting accessibility code.
2780 event.setClassName(this.getClass().getName());
2782 // Identify where the top-left of the screen currently points to.
2783 event.setScrollX(mRenderCoordinates.getScrollXPixInt());
2784 event.setScrollY(mRenderCoordinates.getScrollYPixInt());
2786 // The maximum scroll values are determined by taking the content dimensions and
2787 // subtracting off the actual dimensions of the ChromeView.
2788 int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt());
2789 int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt());
2790 event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0);
2792 // Setting the maximum scroll values requires API level 15 or higher.
2793 final int SDK_VERSION_REQUIRED_TO_SET_SCROLL = 15;
2794 if (Build.VERSION.SDK_INT >= SDK_VERSION_REQUIRED_TO_SET_SCROLL) {
2795 event.setMaxScrollX(maxScrollXPix);
2796 event.setMaxScrollY(maxScrollYPix);
2801 * Returns whether accessibility script injection is enabled on the device
2803 public boolean isDeviceAccessibilityScriptInjectionEnabled() {
2805 // On JellyBean and higher, native accessibility is the default so script
2806 // injection is only allowed if enabled via a flag.
2807 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&
2808 !CommandLine.getInstance().hasSwitch(
2809 ContentSwitches.ENABLE_ACCESSIBILITY_SCRIPT_INJECTION)) {
2813 if (!mContentSettings.getJavaScriptEnabled()) {
2817 int result = getContext().checkCallingOrSelfPermission(
2818 android.Manifest.permission.INTERNET);
2819 if (result != PackageManager.PERMISSION_GRANTED) {
2823 Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
2824 field.setAccessible(true);
2825 String accessibilityScriptInjection = (String) field.get(null);
2826 ContentResolver contentResolver = getContext().getContentResolver();
2828 if (mAccessibilityScriptInjectionObserver == null) {
2829 ContentObserver contentObserver = new ContentObserver(new Handler()) {
2831 public void onChange(boolean selfChange, Uri uri) {
2832 setAccessibilityState(mAccessibilityManager.isEnabled());
2835 contentResolver.registerContentObserver(
2836 Settings.Secure.getUriFor(accessibilityScriptInjection),
2839 mAccessibilityScriptInjectionObserver = contentObserver;
2842 return Settings.Secure.getInt(contentResolver, accessibilityScriptInjection, 0) == 1;
2843 } catch (NoSuchFieldException e) {
2844 // Do nothing, default to false.
2845 } catch (IllegalAccessException e) {
2846 // Do nothing, default to false.
2852 * Returns whether or not accessibility injection is being used.
2854 public boolean isInjectingAccessibilityScript() {
2855 return mAccessibilityInjector.accessibilityIsAvailable();
2859 * Returns true if accessibility is on and touch exploration is enabled.
2861 public boolean isTouchExplorationEnabled() {
2862 return mTouchExplorationEnabled;
2866 * Turns browser accessibility on or off.
2867 * If |state| is |false|, this turns off both native and injected accessibility.
2868 * Otherwise, if accessibility script injection is enabled, this will enable the injected
2869 * accessibility scripts. Native accessibility is enabled on demand.
2871 public void setAccessibilityState(boolean state) {
2873 setInjectedAccessibility(false);
2874 mNativeAccessibilityAllowed = false;
2875 mTouchExplorationEnabled = false;
2877 boolean useScriptInjection = isDeviceAccessibilityScriptInjectionEnabled();
2878 setInjectedAccessibility(useScriptInjection);
2879 mNativeAccessibilityAllowed = !useScriptInjection;
2880 mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
2885 * Enable or disable injected accessibility features
2887 public void setInjectedAccessibility(boolean enabled) {
2888 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
2889 mAccessibilityInjector.setScriptEnabled(enabled);
2893 * Stop any TTS notifications that are currently going on.
2895 public void stopCurrentAccessibilityNotifications() {
2896 mAccessibilityInjector.onPageLostFocus();
2900 * Return whether or not we should set accessibility focus on page load.
2902 public boolean shouldSetAccessibilityFocusOnPageLoad() {
2903 return mShouldSetAccessibilityFocusOnPageLoad;
2907 * Return whether or not we should set accessibility focus on page load.
2908 * This only applies if an accessibility service like TalkBack is running.
2909 * This is desirable behavior for a browser window, but not for an embedded
2912 public void setShouldSetAccessibilityFocusOnPageLoad(boolean on) {
2913 mShouldSetAccessibilityFocusOnPageLoad = on;
2917 * Inform WebKit that Fullscreen mode has been exited by the user.
2919 public void exitFullscreen() {
2920 assert mWebContents != null;
2921 mWebContents.exitFullscreen();
2925 * Changes whether hiding the top controls is enabled.
2927 * @param enableHiding Whether hiding the top controls should be enabled or not.
2928 * @param enableShowing Whether showing the top controls should be enabled or not.
2929 * @param animate Whether the transition should be animated or not.
2931 public void updateTopControlsState(boolean enableHiding, boolean enableShowing,
2933 assert mWebContents != null;
2934 mWebContents.updateTopControlsState(
2935 enableHiding, enableShowing, animate);
2939 * Callback factory method for nativeGetNavigationHistory().
2942 private void addToNavigationHistory(Object history, int index, String url, String virtualUrl,
2943 String originalUrl, String title, Bitmap favicon) {
2944 NavigationEntry entry = new NavigationEntry(
2945 index, url, virtualUrl, originalUrl, title, favicon);
2946 ((NavigationHistory) history).addEntry(entry);
2950 * Get a copy of the navigation history of the view.
2952 public NavigationHistory getNavigationHistory() {
2953 NavigationHistory history = new NavigationHistory();
2954 if (mNativeContentViewCore != 0) {
2955 int currentIndex = nativeGetNavigationHistory(mNativeContentViewCore, history);
2956 history.setCurrentEntryIndex(currentIndex);
2962 public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
2963 NavigationHistory history = new NavigationHistory();
2964 if (mNativeContentViewCore != 0) {
2965 nativeGetDirectedNavigationHistory(
2966 mNativeContentViewCore, history, isForward, itemLimit);
2972 * @return The original request URL for the current navigation entry, or null if there is no
2975 public String getOriginalUrlForActiveNavigationEntry() {
2976 if (mNativeContentViewCore != 0) {
2977 return nativeGetOriginalUrlForActiveNavigationEntry(mNativeContentViewCore);
2983 * @return The cached copy of render positions and scales.
2985 public RenderCoordinates getRenderCoordinates() {
2986 return mRenderCoordinates;
2990 private static Rect createRect(int x, int y, int right, int bottom) {
2991 return new Rect(x, y, right, bottom);
2994 public void extractSmartClipData(int x, int y, int width, int height) {
2995 if (mNativeContentViewCore != 0) {
2996 x += mSmartClipOffsetX;
2997 y += mSmartClipOffsetY;
2998 nativeExtractSmartClipData(mNativeContentViewCore, x, y, width, height);
3003 * Set offsets for smart clip.
3005 * <p>This should be called if there is a viewport change introduced by,
3006 * e.g., show and hide of a location bar.
3008 * @param offsetX Offset for X position.
3009 * @param offsetY Offset for Y position.
3011 public void setSmartClipOffsets(int offsetX, int offsetY) {
3012 mSmartClipOffsetX = offsetX;
3013 mSmartClipOffsetY = offsetY;
3017 private void onSmartClipDataExtracted(String text, String html, Rect clipRect) {
3018 // Translate the positions by the offsets introduced by location bar. Note that the
3019 // coordinates are in dp scale, and that this definitely has the potential to be
3020 // different from the offsets when extractSmartClipData() was called. However,
3021 // as long as OEM has a UI that consumes all the inputs and waits until the
3022 // callback is called, then there shouldn't be any difference.
3023 // TODO(changwan): once crbug.com/416432 is resolved, try to pass offsets as
3024 // separate params for extractSmartClipData(), and apply them not the new offset
3025 // values in the callback.
3026 final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
3027 final int offsetXInDp = (int) (mSmartClipOffsetX / deviceScale);
3028 final int offsetYInDp = (int) (mSmartClipOffsetY / deviceScale);
3029 clipRect.offset(-offsetXInDp, -offsetYInDp);
3031 if (mSmartClipDataListener != null ) {
3032 mSmartClipDataListener.onSmartClipDataExtracted(text, html, clipRect);
3036 public void setSmartClipDataListener(SmartClipDataListener listener) {
3037 mSmartClipDataListener = listener;
3040 public void setBackgroundOpaque(boolean opaque) {
3041 if (mNativeContentViewCore != 0) {
3042 nativeSetBackgroundOpaque(mNativeContentViewCore, opaque);
3047 * Offer a long press gesture to the embedding View, primarily for WebView compatibility.
3049 * @return true if the embedder handled the event.
3051 private boolean offerLongPressToEmbedder() {
3052 return mContainerView.performLongClick();
3056 * Reset scroll and fling accounting, notifying listeners as appropriate.
3057 * This is useful as a failsafe when the input stream may have been interruped.
3059 private void resetScrollInProgress() {
3060 if (!isScrollInProgress()) return;
3062 final boolean touchScrollInProgress = mTouchScrollInProgress;
3063 final int potentiallyActiveFlingCount = mPotentiallyActiveFlingCount;
3065 mTouchScrollInProgress = false;
3066 mPotentiallyActiveFlingCount = 0;
3068 if (touchScrollInProgress) updateGestureStateListener(GestureEventType.SCROLL_END);
3069 if (potentiallyActiveFlingCount > 0) updateGestureStateListener(GestureEventType.FLING_END);
3072 private native long nativeInit(long webContentsPtr,
3073 long viewAndroidPtr, long windowAndroidPtr, HashSet<Object> retainedObjectSet);
3076 private ContentVideoViewClient getContentVideoViewClient() {
3077 return getContentViewClient().getContentVideoViewClient();
3081 private boolean shouldBlockMediaRequest(String url) {
3082 return getContentViewClient().shouldBlockMediaRequest(url);
3086 private void onNativeFlingStopped() {
3087 // Note that mTouchScrollInProgress should normally be false at this
3088 // point, but we reset it anyway as another failsafe.
3089 mTouchScrollInProgress = false;
3090 if (mPotentiallyActiveFlingCount <= 0) return;
3091 mPotentiallyActiveFlingCount--;
3092 updateGestureStateListener(GestureEventType.FLING_END);
3096 public void onScreenOrientationChanged(int orientation) {
3097 sendOrientationChangeEvent(orientation);
3100 public void resumeResponseDeferredAtStart() {
3101 if (mWebContents != null) mWebContents.resumeResponseDeferredAtStart();
3105 * Set whether the ContentViewCore requires the WebContents to be fullscreen in order to lock
3106 * the screen orientation.
3108 public void setFullscreenRequiredForOrientationLock(boolean value) {
3109 mFullscreenRequiredForOrientationLock = value;
3113 private boolean isFullscreenRequiredForOrientationLock() {
3114 return mFullscreenRequiredForOrientationLock;
3117 private native WebContents nativeGetWebContentsAndroid(long nativeContentViewCoreImpl);
3119 private native void nativeOnJavaContentViewCoreDestroyed(long nativeContentViewCoreImpl);
3121 private native void nativeLoadUrl(
3122 long nativeContentViewCoreImpl,
3128 int uaOverrideOption,
3129 String extraHeaders,
3131 String baseUrlForDataUrl,
3132 String virtualUrlForDataUrl,
3133 boolean canLoadLocalResources,
3134 boolean isRendererInitiated);
3136 private native void nativeSetFocus(long nativeContentViewCoreImpl, boolean focused);
3138 private native void nativeSendOrientationChangeEvent(
3139 long nativeContentViewCoreImpl, int orientation);
3141 // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
3142 private native boolean nativeOnTouchEvent(
3143 long nativeContentViewCoreImpl, MotionEvent event,
3144 long timeMs, int action, int pointerCount, int historySize, int actionIndex,
3145 float x0, float y0, float x1, float y1,
3146 int pointerId0, int pointerId1,
3147 float touchMajor0, float touchMajor1,
3148 float rawX, float rawY,
3149 int androidToolType0, int androidToolType1, int androidButtonState,
3150 boolean isTouchHandleEvent);
3152 private native int nativeSendMouseMoveEvent(
3153 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3155 private native int nativeSendMouseWheelEvent(
3156 long nativeContentViewCoreImpl, long timeMs, float x, float y, float verticalAxis);
3158 private native void nativeScrollBegin(
3159 long nativeContentViewCoreImpl, long timeMs, float x, float y, float hintX,
3162 private native void nativeScrollEnd(long nativeContentViewCoreImpl, long timeMs);
3164 private native void nativeScrollBy(
3165 long nativeContentViewCoreImpl, long timeMs, float x, float y,
3166 float deltaX, float deltaY);
3168 private native void nativeFlingStart(
3169 long nativeContentViewCoreImpl, long timeMs, float x, float y, float vx, float vy);
3171 private native void nativeFlingCancel(long nativeContentViewCoreImpl, long timeMs);
3173 private native void nativeSingleTap(
3174 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3176 private native void nativeDoubleTap(
3177 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3179 private native void nativeLongPress(
3180 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3182 private native void nativePinchBegin(
3183 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3185 private native void nativePinchEnd(long nativeContentViewCoreImpl, long timeMs);
3187 private native void nativePinchBy(long nativeContentViewCoreImpl, long timeMs,
3188 float anchorX, float anchorY, float deltaScale);
3190 private native void nativeSelectBetweenCoordinates(
3191 long nativeContentViewCoreImpl, float x1, float y1, float x2, float y2);
3193 private native void nativeMoveCaret(long nativeContentViewCoreImpl, float x, float y);
3195 private native void nativeHideTextHandles(long nativeContentViewCoreImpl);
3197 private native void nativeResetGestureDetection(long nativeContentViewCoreImpl);
3199 private native void nativeSetDoubleTapSupportEnabled(
3200 long nativeContentViewCoreImpl, boolean enabled);
3202 private native void nativeSetMultiTouchZoomSupportEnabled(
3203 long nativeContentViewCoreImpl, boolean enabled);
3205 private native void nativeSelectPopupMenuItems(long nativeContentViewCoreImpl, int[] indices);
3207 private native void nativeClearHistory(long nativeContentViewCoreImpl);
3209 private native void nativePostMessageToFrame(long nativeContentViewCoreImpl, String frameId,
3210 String message, String sourceOrigin, String targetOrigin);
3212 private native long nativeGetNativeImeAdapter(long nativeContentViewCoreImpl);
3214 private native int nativeGetCurrentRenderProcessId(long nativeContentViewCoreImpl);
3216 private native void nativeSetUseDesktopUserAgent(long nativeContentViewCoreImpl,
3217 boolean enabled, boolean reloadOnChange);
3219 private native boolean nativeGetUseDesktopUserAgent(long nativeContentViewCoreImpl);
3221 private native void nativeClearSslPreferences(long nativeContentViewCoreImpl);
3223 private native void nativeSetAllowJavascriptInterfacesInspection(
3224 long nativeContentViewCoreImpl, boolean allow);
3226 private native void nativeAddJavascriptInterface(long nativeContentViewCoreImpl, Object object,
3227 String name, Class requiredAnnotation);
3229 private native void nativeRemoveJavascriptInterface(long nativeContentViewCoreImpl,
3232 private native int nativeGetNavigationHistory(long nativeContentViewCoreImpl, Object context);
3234 private native void nativeGetDirectedNavigationHistory(long nativeContentViewCoreImpl,
3235 Object context, boolean isForward, int maxEntries);
3237 private native String nativeGetOriginalUrlForActiveNavigationEntry(
3238 long nativeContentViewCoreImpl);
3240 private native void nativeWasResized(long nativeContentViewCoreImpl);
3242 private native void nativeSetAccessibilityEnabled(
3243 long nativeContentViewCoreImpl, boolean enabled);
3245 private native void nativeExtractSmartClipData(long nativeContentViewCoreImpl,
3246 int x, int y, int w, int h);
3248 private native void nativeSetBackgroundOpaque(long nativeContentViewCoreImpl, boolean opaque);