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.app.Activity;
8 import android.app.SearchManager;
9 import android.content.ContentResolver;
10 import android.content.Context;
11 import android.content.Intent;
12 import android.content.pm.PackageManager;
13 import android.content.res.Configuration;
14 import android.database.ContentObserver;
15 import android.graphics.Bitmap;
16 import android.graphics.Canvas;
17 import android.graphics.Color;
18 import android.graphics.Rect;
19 import android.graphics.RectF;
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.TextUtils;
30 import android.util.Log;
31 import android.util.Pair;
32 import android.view.ActionMode;
33 import android.view.HapticFeedbackConstants;
34 import android.view.InputDevice;
35 import android.view.KeyEvent;
36 import android.view.MotionEvent;
37 import android.view.Surface;
38 import android.view.View;
39 import android.view.ViewGroup;
40 import android.view.WindowManager;
41 import android.view.accessibility.AccessibilityEvent;
42 import android.view.accessibility.AccessibilityManager;
43 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
44 import android.view.accessibility.AccessibilityNodeInfo;
45 import android.view.accessibility.AccessibilityNodeProvider;
46 import android.view.inputmethod.EditorInfo;
47 import android.view.inputmethod.InputConnection;
48 import android.view.inputmethod.InputMethodManager;
49 import android.widget.AbsoluteLayout;
50 import android.widget.FrameLayout;
52 import com.google.common.annotations.VisibleForTesting;
54 import org.chromium.base.CalledByNative;
55 import org.chromium.base.CommandLine;
56 import org.chromium.base.JNINamespace;
57 import org.chromium.base.ObserverList;
58 import org.chromium.base.ObserverList.RewindableIterator;
59 import org.chromium.base.TraceEvent;
60 import org.chromium.base.WeakContext;
61 import org.chromium.content.R;
62 import org.chromium.content.browser.ContentViewGestureHandler.MotionEventDelegate;
63 import org.chromium.content.browser.accessibility.AccessibilityInjector;
64 import org.chromium.content.browser.accessibility.BrowserAccessibilityManager;
65 import org.chromium.content.browser.input.AdapterInputConnection;
66 import org.chromium.content.browser.input.HandleView;
67 import org.chromium.content.browser.input.ImeAdapter;
68 import org.chromium.content.browser.input.ImeAdapter.AdapterInputConnectionFactory;
69 import org.chromium.content.browser.input.InputMethodManagerWrapper;
70 import org.chromium.content.browser.input.InsertionHandleController;
71 import org.chromium.content.browser.input.SelectPopupDialog;
72 import org.chromium.content.browser.input.SelectPopupItem;
73 import org.chromium.content.browser.input.SelectionHandleController;
74 import org.chromium.content.common.ContentSwitches;
75 import org.chromium.content_public.browser.GestureStateListener;
76 import org.chromium.content_public.browser.WebContents;
77 import org.chromium.ui.base.ViewAndroid;
78 import org.chromium.ui.base.ViewAndroidDelegate;
79 import org.chromium.ui.base.WindowAndroid;
80 import org.chromium.ui.gfx.DeviceDisplayInfo;
82 import java.lang.annotation.Annotation;
83 import java.lang.reflect.Field;
84 import java.util.ArrayList;
85 import java.util.HashMap;
86 import java.util.HashSet;
87 import java.util.List;
91 * Provides a Java-side 'wrapper' around a WebContent (native) instance.
92 * Contains all the major functionality necessary to manage the lifecycle of a ContentView without
93 * being tied to the view system.
95 @JNINamespace("content")
96 public class ContentViewCore
97 implements MotionEventDelegate, NavigationClient, AccessibilityStateChangeListener {
99 private static final String TAG = "ContentViewCore";
101 // Used to avoid enabling zooming in / out if resulting zooming will
102 // produce little visible difference.
103 private static final float ZOOM_CONTROLS_EPSILON = 0.007f;
105 // Used to represent gestures for long press and long tap.
106 private static final int IS_LONG_PRESS = 1;
107 private static final int IS_LONG_TAP = 2;
109 // Length of the delay (in ms) before fading in handles after the last page movement.
110 private static final int TEXT_HANDLE_FADE_IN_DELAY = 300;
112 // If the embedder adds a JavaScript interface object that contains an indirect reference to
113 // the ContentViewCore, then storing a strong ref to the interface object on the native
114 // side would prevent garbage collection of the ContentViewCore (as that strong ref would
115 // create a new GC root).
116 // For that reason, we store only a weak reference to the interface object on the
117 // native side. However we still need a strong reference on the Java side to
118 // prevent garbage collection if the embedder doesn't maintain their own ref to the
119 // interface object - the Java side ref won't create a new GC root.
120 // This map stores those refernces. We put into the map on addJavaScriptInterface()
121 // and remove from it in removeJavaScriptInterface().
122 private final Map<String, Object> mJavaScriptInterfaces = new HashMap<String, Object>();
124 // Additionally, we keep track of all Java bound JS objects that are in use on the
125 // current page to ensure that they are not garbage collected until the page is
126 // navigated. This includes interface objects that have been removed
127 // via the removeJavaScriptInterface API and transient objects returned from methods
128 // on the interface object. Note we use HashSet rather than Set as the native side
129 // expects HashSet (no bindings for interfaces).
130 private final HashSet<Object> mRetainedJavaScriptObjects = new HashSet<Object>();
133 * Interface that consumers of {@link ContentViewCore} must implement to allow the proper
134 * dispatching of view methods through the containing view.
137 * All methods with the "super_" prefix should be routed to the parent of the
138 * implementing container view.
140 @SuppressWarnings("javadoc")
141 public interface InternalAccessDelegate {
143 * @see View#drawChild(Canvas, View, long)
145 boolean drawChild(Canvas canvas, View child, long drawingTime);
148 * @see View#onKeyUp(keyCode, KeyEvent)
150 boolean super_onKeyUp(int keyCode, KeyEvent event);
153 * @see View#dispatchKeyEventPreIme(KeyEvent)
155 boolean super_dispatchKeyEventPreIme(KeyEvent event);
158 * @see View#dispatchKeyEvent(KeyEvent)
160 boolean super_dispatchKeyEvent(KeyEvent event);
163 * @see View#onGenericMotionEvent(MotionEvent)
165 boolean super_onGenericMotionEvent(MotionEvent event);
168 * @see View#onConfigurationChanged(Configuration)
170 void super_onConfigurationChanged(Configuration newConfig);
173 * @see View#onScrollChanged(int, int, int, int)
175 void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix);
178 * @see View#awakenScrollBars()
180 boolean awakenScrollBars();
183 * @see View#awakenScrollBars(int, boolean)
185 boolean super_awakenScrollBars(int startDelay, boolean invalidate);
189 * An interface for controlling visibility and state of embedder-provided zoom controls.
191 public interface ZoomControlsDelegate {
193 * Called when it's reasonable to show zoom controls.
195 void invokeZoomPicker();
198 * Called when zoom controls need to be hidden (e.g. when the view hides).
200 void dismissZoomPicker();
203 * Called when page scale has been changed, so the controls can update their state.
205 void updateZoomControls();
209 * An interface that allows the embedder to be notified when the results of
210 * extractSmartClipData are available.
212 public interface SmartClipDataListener {
213 public void onSmartClipDataExtracted(String result);
216 private VSyncManager.Provider mVSyncProvider;
217 private VSyncManager.Listener mVSyncListener;
218 private int mVSyncSubscriberCount;
219 private boolean mVSyncListenerRegistered;
221 // To avoid IPC delay we use input events to directly trigger a vsync signal in the renderer.
222 // When we do this, we also need to avoid sending the real vsync signal for the current
223 // frame to avoid double-ticking. This flag is used to inhibit the next vsync notification.
224 private boolean mDidSignalVSyncUsingInputEvent;
226 public VSyncManager.Listener getVSyncListener(VSyncManager.Provider vsyncProvider) {
227 if (mVSyncProvider != null && mVSyncListenerRegistered) {
228 mVSyncProvider.unregisterVSyncListener(mVSyncListener);
229 mVSyncListenerRegistered = false;
232 mVSyncProvider = vsyncProvider;
233 mVSyncListener = new VSyncManager.Listener() {
235 public void updateVSync(long tickTimeMicros, long intervalMicros) {
236 if (mNativeContentViewCore != 0) {
237 nativeUpdateVSyncParameters(mNativeContentViewCore, tickTimeMicros,
243 public void onVSync(long frameTimeMicros) {
244 animateIfNecessary(frameTimeMicros);
246 if (mRequestedVSyncForInput) {
247 mRequestedVSyncForInput = false;
248 removeVSyncSubscriber();
250 if (mNativeContentViewCore != 0) {
251 nativeOnVSync(mNativeContentViewCore, frameTimeMicros);
256 if (mVSyncSubscriberCount > 0) {
257 // addVSyncSubscriber() is called before getVSyncListener.
258 vsyncProvider.registerVSyncListener(mVSyncListener);
259 mVSyncListenerRegistered = true;
262 return mVSyncListener;
266 void addVSyncSubscriber() {
267 if (!isVSyncNotificationEnabled()) {
268 mDidSignalVSyncUsingInputEvent = false;
270 if (mVSyncProvider != null && !mVSyncListenerRegistered) {
271 mVSyncProvider.registerVSyncListener(mVSyncListener);
272 mVSyncListenerRegistered = true;
274 mVSyncSubscriberCount++;
278 void removeVSyncSubscriber() {
279 if (mVSyncProvider != null && mVSyncSubscriberCount == 1) {
280 assert mVSyncListenerRegistered;
281 mVSyncProvider.unregisterVSyncListener(mVSyncListener);
282 mVSyncListenerRegistered = false;
284 mVSyncSubscriberCount--;
285 assert mVSyncSubscriberCount >= 0;
289 private void resetVSyncNotification() {
290 while (isVSyncNotificationEnabled()) removeVSyncSubscriber();
291 mVSyncSubscriberCount = 0;
292 mVSyncListenerRegistered = false;
293 mNeedAnimate = false;
296 private boolean isVSyncNotificationEnabled() {
297 return mVSyncProvider != null && mVSyncListenerRegistered;
301 private void setNeedsAnimate() {
304 addVSyncSubscriber();
308 private final Context mContext;
309 private ViewGroup mContainerView;
310 private InternalAccessDelegate mContainerViewInternals;
311 private WebContents mWebContents;
312 private WebContentsObserverAndroid mWebContentsObserver;
314 private ContentViewClient mContentViewClient;
316 private ContentSettings mContentSettings;
318 // Native pointer to C++ ContentViewCoreImpl object which will be set by nativeInit().
319 private long mNativeContentViewCore = 0;
321 private boolean mInForeground = false;
323 private ContentViewGestureHandler mContentViewGestureHandler;
324 private final ObserverList<GestureStateListener> mGestureStateListeners;
325 private final RewindableIterator<GestureStateListener> mGestureStateListenersIterator;
326 private ZoomControlsDelegate mZoomControlsDelegate;
328 private PopupZoomer mPopupZoomer;
330 private Runnable mFakeMouseMoveRunnable = null;
332 // Only valid when focused on a text / password field.
333 private ImeAdapter mImeAdapter;
334 private ImeAdapter.AdapterInputConnectionFactory mAdapterInputConnectionFactory;
335 private AdapterInputConnection mInputConnection;
337 private SelectionHandleController mSelectionHandleController;
338 private InsertionHandleController mInsertionHandleController;
340 private Runnable mDeferredHandleFadeInRunnable;
342 private PositionObserver mPositionObserver;
343 private PositionObserver.Listener mPositionListener;
345 // Size of the viewport in physical pixels as set from onSizeChanged.
346 private int mViewportWidthPix;
347 private int mViewportHeightPix;
348 private int mPhysicalBackingWidthPix;
349 private int mPhysicalBackingHeightPix;
350 private int mOverdrawBottomHeightPix;
351 private int mViewportSizeOffsetWidthPix;
352 private int mViewportSizeOffsetHeightPix;
353 private int mLocationInWindowX;
354 private int mLocationInWindowY;
356 // Cached copy of all positions and scales as reported by the renderer.
357 private final RenderCoordinates mRenderCoordinates;
359 private final RenderCoordinates.NormalizedPoint mStartHandlePoint;
360 private final RenderCoordinates.NormalizedPoint mEndHandlePoint;
361 private final RenderCoordinates.NormalizedPoint mInsertionHandlePoint;
363 // Tracks whether a selection is currently active. When applied to selected text, indicates
364 // whether the last selected text is still highlighted.
365 private boolean mHasSelection;
366 private String mLastSelectedText;
367 private boolean mSelectionEditable;
368 private ActionMode mActionMode;
369 private boolean mUnselectAllOnActionModeDismiss;
371 // Delegate that will handle GET downloads, and be notified of completion of POST downloads.
372 private ContentViewDownloadDelegate mDownloadDelegate;
374 // The AccessibilityInjector that handles loading Accessibility scripts into the web page.
375 private AccessibilityInjector mAccessibilityInjector;
377 // Whether native accessibility, i.e. without any script injection, is allowed.
378 private boolean mNativeAccessibilityAllowed;
380 // Whether native accessibility, i.e. without any script injection, has been enabled.
381 private boolean mNativeAccessibilityEnabled;
383 // Handles native accessibility, i.e. without any script injection.
384 private BrowserAccessibilityManager mBrowserAccessibilityManager;
386 // System accessibility service.
387 private final AccessibilityManager mAccessibilityManager;
389 // Allows us to dynamically respond when the accessibility script injection flag changes.
390 private ContentObserver mAccessibilityScriptInjectionObserver;
392 // Temporary notification to tell onSizeChanged to focus a form element,
393 // because the OSK was just brought up.
394 private boolean mUnfocusOnNextSizeChanged = false;
395 private final Rect mFocusPreOSKViewportRect = new Rect();
397 // Used to keep track of whether we should try to undo the last zoom-to-textfield operation.
398 private boolean mScrolledAndZoomedFocusedEditableNode = false;
400 // Whether we received a new frame since consumePendingRendererFrame() was last called.
401 private boolean mPendingRendererFrame = false;
403 // Whether we should animate at the next vsync tick.
404 private boolean mNeedAnimate = false;
406 // Whether we requested a proactive vsync event in response to touch input.
407 // This reduces the latency of responding to input by ensuring the renderer
408 // is sent a BeginFrame for every touch event we receive. Otherwise the
409 // renderer's SetNeedsBeginFrame message would get serviced at the next
411 private boolean mRequestedVSyncForInput = false;
413 // Used for tracking UMA ActionAfterDoubleTap to tell user's immediate
414 // action after a double tap.
415 private long mLastDoubleTapTimeMs;
417 // On single tap this will store the x, y coordinates of the touch.
418 private int mSingleTapX;
419 private int mSingleTapY;
421 private ViewAndroid mViewAndroid;
423 private SmartClipDataListener mSmartClipDataListener = null;
425 /** ActionAfterDoubleTap defined in tools/metrics/histograms/histograms.xml. */
426 private static class UMAActionAfterDoubleTap {
427 public static final int NAVIGATE_BACK = 0;
428 public static final int NAVIGATE_STOP = 1;
429 public static final int NO_ACTION = 2;
430 public static final int COUNT = 3;
433 /** TapDelayType defined in tools/metrics/histograms/histograms.xml. */
434 private static class UMASingleTapType {
435 public static final int DELAYED_TAP = 0;
436 public static final int UNDELAYED_TAP = 1;
437 public static final int COUNT = 2;
441 * Used by UMA stat for tracking accidental double tap navigations. Specifies the amount of
442 * time after a double tap within which actions will be recorded to the UMA stat.
444 private static final long ACTION_AFTER_DOUBLE_TAP_WINDOW_MS = 5000;
447 * Constructs a new ContentViewCore. Embedders must call initialize() after constructing
448 * a ContentViewCore and before using it.
450 * @param context The context used to create this.
452 public ContentViewCore(Context context) {
455 WeakContext.initializeWeakContext(context);
456 HeapStatsLogger.init(mContext.getApplicationContext());
457 mAdapterInputConnectionFactory = new AdapterInputConnectionFactory();
459 mRenderCoordinates = new RenderCoordinates();
460 mRenderCoordinates.setDeviceScaleFactor(
461 getContext().getResources().getDisplayMetrics().density);
462 mStartHandlePoint = mRenderCoordinates.createNormalizedPoint();
463 mEndHandlePoint = mRenderCoordinates.createNormalizedPoint();
464 mInsertionHandlePoint = mRenderCoordinates.createNormalizedPoint();
465 mAccessibilityManager = (AccessibilityManager)
466 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
467 mGestureStateListeners = new ObserverList<GestureStateListener>();
468 mGestureStateListenersIterator = mGestureStateListeners.rewindableIterator();
472 * @return The context used for creating this ContentViewCore.
475 public Context getContext() {
480 * @return The ViewGroup that all view actions of this ContentViewCore should interact with.
482 public ViewGroup getContainerView() {
483 return mContainerView;
487 * @return The WebContents currently being rendered.
489 public WebContents getWebContents() {
494 * Specifies how much smaller the WebKit layout size should be relative to the size of this
496 * @param offsetXPix The X amount in pixels to shrink the viewport by.
497 * @param offsetYPix The Y amount in pixels to shrink the viewport by.
499 public void setViewportSizeOffset(int offsetXPix, int offsetYPix) {
500 if (offsetXPix != mViewportSizeOffsetWidthPix ||
501 offsetYPix != mViewportSizeOffsetHeightPix) {
502 mViewportSizeOffsetWidthPix = offsetXPix;
503 mViewportSizeOffsetHeightPix = offsetYPix;
504 if (mNativeContentViewCore != 0) nativeWasResized(mNativeContentViewCore);
509 * Returns a delegate that can be used to add and remove views from the ContainerView.
511 * NOTE: Use with care, as not all ContentViewCore users setup their ContainerView in the same
512 * way. In particular, the Android WebView has limitations on what implementation details can
513 * be provided via a child view, as they are visible in the API and could introduce
514 * compatibility breaks with existing applications. If in doubt, contact the
515 * android_webview/OWNERS
517 * @return A ViewAndroidDelegate that can be used to add and remove views.
520 public ViewAndroidDelegate getViewAndroidDelegate() {
521 return new ViewAndroidDelegate() {
523 public View acquireAnchorView() {
524 View anchorView = new View(getContext());
525 mContainerView.addView(anchorView);
530 @SuppressWarnings("deprecation") // AbsoluteLayout.LayoutParams
531 public void setAnchorViewPosition(
532 View view, float x, float y, float width, float height) {
533 assert view.getParent() == mContainerView;
535 float scale = (float) DeviceDisplayInfo.create(getContext()).getDIPScale();
537 // The anchor view should not go outside the bounds of the ContainerView.
538 int leftMargin = Math.round(x * scale);
539 int topMargin = Math.round(mRenderCoordinates.getContentOffsetYPix() + y * scale);
540 int scaledWidth = Math.round(width * scale);
541 // ContentViewCore currently only supports these two container view types.
542 if (mContainerView instanceof FrameLayout) {
543 if (scaledWidth + leftMargin > mContainerView.getWidth()) {
544 scaledWidth = mContainerView.getWidth() - leftMargin;
546 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
547 scaledWidth, Math.round(height * scale));
548 lp.leftMargin = leftMargin;
549 lp.topMargin = topMargin;
550 view.setLayoutParams(lp);
551 } else if (mContainerView instanceof AbsoluteLayout) {
552 // This fixes the offset due to a difference in
553 // scrolling model of WebView vs. Chrome.
554 // TODO(sgurun) fix this to use mContainerView.getScroll[X/Y]()
555 // as it naturally accounts for scroll differences between
557 leftMargin += mRenderCoordinates.getScrollXPixInt();
558 topMargin += mRenderCoordinates.getScrollYPixInt();
560 android.widget.AbsoluteLayout.LayoutParams lp =
561 new android.widget.AbsoluteLayout.LayoutParams(
562 scaledWidth, (int) (height * scale), leftMargin, topMargin);
563 view.setLayoutParams(lp);
565 Log.e(TAG, "Unknown layout " + mContainerView.getClass().getName());
570 public void releaseAnchorView(View anchorView) {
571 mContainerView.removeView(anchorView);
577 public void setImeAdapterForTest(ImeAdapter imeAdapter) {
578 mImeAdapter = imeAdapter;
582 public ImeAdapter getImeAdapterForTest() {
587 public void setAdapterInputConnectionFactory(AdapterInputConnectionFactory factory) {
588 mAdapterInputConnectionFactory = factory;
592 public AdapterInputConnection getInputConnectionForTest() {
593 return mInputConnection;
596 private ImeAdapter createImeAdapter(Context context) {
597 return new ImeAdapter(new InputMethodManagerWrapper(context),
598 new ImeAdapter.ImeAdapterDelegate() {
600 public void onImeEvent(boolean isFinish) {
601 getContentViewClient().onImeEvent();
604 undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
609 public void onSetFieldValue() {
610 scrollFocusedEditableNodeIntoView();
614 public void onDismissInput() {
615 getContentViewClient().onImeStateChangeRequested(false);
619 public View getAttachedView() {
620 return mContainerView;
624 public ResultReceiver getNewShowKeyboardReceiver() {
625 return new ResultReceiver(new Handler()) {
627 public void onReceiveResult(int resultCode, Bundle resultData) {
628 getContentViewClient().onImeStateChangeRequested(
629 resultCode == InputMethodManager.RESULT_SHOWN ||
630 resultCode == InputMethodManager.RESULT_UNCHANGED_SHOWN);
631 if (resultCode == InputMethodManager.RESULT_SHOWN) {
632 // If OSK is newly shown, delay the form focus until
633 // the onSizeChanged (in order to adjust relative to the
635 // TODO(jdduke): We should not assume that onSizeChanged will
636 // always be called, crbug.com/294908.
637 getContainerView().getWindowVisibleDisplayFrame(
638 mFocusPreOSKViewportRect);
639 } else if (resultCode ==
640 InputMethodManager.RESULT_UNCHANGED_SHOWN) {
641 // If the OSK was already there, focus the form immediately.
642 scrollFocusedEditableNodeIntoView();
644 undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
655 * @param containerView The view that will act as a container for all views created by this.
656 * @param internalDispatcher Handles dispatching all hidden or super methods to the
658 * @param nativeWebContents A pointer to the native web contents.
659 * @param windowAndroid An instance of the WindowAndroid.
661 // Perform important post-construction set up of the ContentViewCore.
662 // We do not require the containing view in the constructor to allow embedders to create a
663 // ContentViewCore without having fully created its containing view. The containing view
664 // is a vital component of the ContentViewCore, so embedders must exercise caution in what
665 // they do with the ContentViewCore before calling initialize().
666 // We supply the nativeWebContents pointer here rather than in the constructor to allow us
667 // to set the private browsing mode at a later point for the WebView implementation.
668 // Note that the caller remains the owner of the nativeWebContents and is responsible for
669 // deleting it after destroying the ContentViewCore.
670 public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
671 long nativeWebContents, WindowAndroid windowAndroid) {
672 mContainerView = containerView;
673 mPositionObserver = new ViewPositionObserver(mContainerView);
674 mPositionListener = new PositionObserver.Listener() {
676 public void onPositionChanged(int x, int y) {
677 if (isSelectionHandleShowing() || isInsertionHandleShowing()) {
678 temporarilyHideTextHandles();
683 long windowNativePointer = windowAndroid != null ? windowAndroid.getNativePointer() : 0;
685 long viewAndroidNativePointer = 0;
686 if (windowNativePointer != 0) {
687 mViewAndroid = new ViewAndroid(windowAndroid, getViewAndroidDelegate());
688 viewAndroidNativePointer = mViewAndroid.getNativePointer();
691 // Note ContentViewGestureHandler initialization must occur before nativeInit
692 // because nativeInit may callback into hasTouchEventHandlers.
693 mContentViewGestureHandler = new ContentViewGestureHandler(mContext, this);
694 mZoomControlsDelegate = new ZoomControlsDelegate() {
696 public void invokeZoomPicker() {}
698 public void dismissZoomPicker() {}
700 public void updateZoomControls() {}
703 mNativeContentViewCore = nativeInit(
704 nativeWebContents, viewAndroidNativePointer, windowNativePointer);
705 mWebContents = nativeGetWebContentsAndroid(mNativeContentViewCore);
706 mContentSettings = new ContentSettings(this, mNativeContentViewCore);
707 initializeContainerView(internalDispatcher);
709 mAccessibilityInjector = AccessibilityInjector.newInstance(this);
711 String contentDescription = "Web View";
712 if (R.string.accessibility_content_view == 0) {
713 Log.w(TAG, "Setting contentDescription to 'Web View' as no value was specified.");
715 contentDescription = mContext.getResources().getString(
716 R.string.accessibility_content_view);
718 mContainerView.setContentDescription(contentDescription);
719 mWebContentsObserver = new WebContentsObserverAndroid(this) {
721 public void didStartLoading(String url) {
723 resetGestureDetectors();
727 sendOrientationChangeEvent();
731 void onNativeContentViewCoreDestroyed(long nativeContentViewCore) {
732 assert nativeContentViewCore == mNativeContentViewCore;
733 mNativeContentViewCore = 0;
737 * Set the Container view Internals.
738 * @param internalDispatcher Handles dispatching all hidden or super methods to the
741 public void setContainerViewInternals(InternalAccessDelegate internalDispatcher) {
742 mContainerViewInternals = internalDispatcher;
746 * Initializes the View that will contain all Views created by the ContentViewCore.
748 * @param internalDispatcher Handles dispatching all hidden or super methods to the
751 private void initializeContainerView(InternalAccessDelegate internalDispatcher) {
753 mContainerViewInternals = internalDispatcher;
755 mContainerView.setWillNotDraw(false);
756 mContainerView.setClickable(true);
758 mRenderCoordinates.reset();
759 onRenderCoordinatesUpdated();
761 initPopupZoomer(mContext);
762 mImeAdapter = createImeAdapter(mContext);
766 private void initPopupZoomer(Context context) {
767 mPopupZoomer = new PopupZoomer(context);
768 mPopupZoomer.setOnVisibilityChangedListener(new PopupZoomer.OnVisibilityChangedListener() {
770 public void onPopupZoomerShown(final PopupZoomer zoomer) {
771 mContainerView.post(new Runnable() {
774 if (mContainerView.indexOfChild(zoomer) == -1) {
775 mContainerView.addView(zoomer);
777 assert false : "PopupZoomer should never be shown without being hidden";
784 public void onPopupZoomerHidden(final PopupZoomer zoomer) {
785 mContainerView.post(new Runnable() {
788 if (mContainerView.indexOfChild(zoomer) != -1) {
789 mContainerView.removeView(zoomer);
790 mContainerView.invalidate();
792 assert false : "PopupZoomer should never be hidden without being shown";
798 // TODO(yongsheng): LONG_TAP is not enabled in PopupZoomer. So need to dispatch a LONG_TAP
799 // gesture if a user completes a tap on PopupZoomer UI after a LONG_PRESS gesture.
800 PopupZoomer.OnTapListener listener = new PopupZoomer.OnTapListener() {
802 public boolean onSingleTap(View v, MotionEvent e) {
803 mContainerView.requestFocus();
804 if (mNativeContentViewCore != 0) {
805 nativeSingleTap(mNativeContentViewCore, e.getEventTime(),
806 e.getX(), e.getY(), true);
812 public boolean onLongPress(View v, MotionEvent e) {
813 if (mNativeContentViewCore != 0) {
814 nativeLongPress(mNativeContentViewCore, e.getEventTime(),
815 e.getX(), e.getY(), true);
820 mPopupZoomer.setOnTapListener(listener);
824 * Destroy the internal state of the ContentView. This method may only be
825 * called after the ContentView has been removed from the view system. No
826 * other methods may be called on this ContentView after this method has
829 public void destroy() {
830 if (mNativeContentViewCore != 0) {
831 nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore);
834 resetVSyncNotification();
835 mVSyncProvider = null;
836 if (mViewAndroid != null) mViewAndroid.destroy();
837 mNativeContentViewCore = 0;
838 mContentSettings = null;
839 mJavaScriptInterfaces.clear();
840 mRetainedJavaScriptObjects.clear();
841 unregisterAccessibilityContentObserver();
842 mGestureStateListeners.clear();
845 private void unregisterAccessibilityContentObserver() {
846 if (mAccessibilityScriptInjectionObserver == null) {
849 getContext().getContentResolver().unregisterContentObserver(
850 mAccessibilityScriptInjectionObserver);
851 mAccessibilityScriptInjectionObserver = null;
855 * Returns true initially, false after destroy() has been called.
856 * It is illegal to call any other public method after destroy().
858 public boolean isAlive() {
859 return mNativeContentViewCore != 0;
863 * This is only useful for passing over JNI to native code that requires ContentViewCore*.
864 * @return native ContentViewCore pointer.
867 public long getNativeContentViewCore() {
868 return mNativeContentViewCore;
871 public void setContentViewClient(ContentViewClient client) {
872 if (client == null) {
873 throw new IllegalArgumentException("The client can't be null.");
875 mContentViewClient = client;
878 ContentViewClient getContentViewClient() {
879 if (mContentViewClient == null) {
880 // We use the Null Object pattern to avoid having to perform a null check in this class.
881 // We create it lazily because most of the time a client will be set almost immediately
882 // after ContentView is created.
883 mContentViewClient = new ContentViewClient();
884 // We don't set the native ContentViewClient pointer here on purpose. The native
885 // implementation doesn't mind a null delegate and using one is better than passing a
886 // Null Object, since we cut down on the number of JNI calls.
888 return mContentViewClient;
891 public int getBackgroundColor() {
892 if (mNativeContentViewCore != 0) {
893 return nativeGetBackgroundColor(mNativeContentViewCore);
899 private void onBackgroundColorChanged(int color) {
900 getContentViewClient().onBackgroundColorChanged(color);
904 * Load url without fixing up the url string. Consumers of ContentView are responsible for
905 * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
906 * off during user input).
908 * @param params Parameters for this load.
910 public void loadUrl(LoadUrlParams params) {
911 if (mNativeContentViewCore == 0) return;
913 nativeLoadUrl(mNativeContentViewCore,
916 params.mTransitionType,
917 params.mUaOverrideOption,
918 params.getExtraHeadersString(),
920 params.mBaseUrlForDataUrl,
921 params.mVirtualUrlForDataUrl,
922 params.mCanLoadLocalResources);
926 * Stops loading the current web contents.
928 public void stopLoading() {
929 reportActionAfterDoubleTapUMA(UMAActionAfterDoubleTap.NAVIGATE_STOP);
930 if (mNativeContentViewCore != 0) nativeStopLoading(mNativeContentViewCore);
934 * Get the URL of the current page.
936 * @return The URL of the current page.
938 public String getUrl() {
939 if (mNativeContentViewCore != 0) return nativeGetURL(mNativeContentViewCore);
944 * Get the title of the current page.
946 * @return The title of the current page.
948 public String getTitle() {
949 if (mNativeContentViewCore != 0) return nativeGetTitle(mNativeContentViewCore);
954 * Shows an interstitial page driven by the passed in delegate.
956 * @param url The URL being blocked by the interstitial.
957 * @param delegate The delegate handling the interstitial.
960 public void showInterstitialPage(
961 String url, InterstitialPageDelegateAndroid delegate) {
962 if (mNativeContentViewCore == 0) return;
963 nativeShowInterstitialPage(mNativeContentViewCore, url, delegate.getNative());
967 * @return Whether the page is currently showing an interstitial, such as a bad HTTPS page.
969 public boolean isShowingInterstitialPage() {
970 return mNativeContentViewCore == 0 ?
971 false : nativeIsShowingInterstitialPage(mNativeContentViewCore);
975 * Mark any new frames that have arrived since this function was last called as non-pending.
977 * @return Whether there was a pending frame from the renderer.
979 public boolean consumePendingRendererFrame() {
980 boolean hadPendingFrame = mPendingRendererFrame;
981 mPendingRendererFrame = false;
982 return hadPendingFrame;
986 * @return Viewport width in physical pixels as set from onSizeChanged.
989 public int getViewportWidthPix() { return mViewportWidthPix; }
992 * @return Viewport height in physical pixels as set from onSizeChanged.
995 public int getViewportHeightPix() { return mViewportHeightPix; }
998 * @return Width of underlying physical surface.
1001 public int getPhysicalBackingWidthPix() { return mPhysicalBackingWidthPix; }
1004 * @return Height of underlying physical surface.
1007 public int getPhysicalBackingHeightPix() { return mPhysicalBackingHeightPix; }
1010 * @return Amount the output surface extends past the bottom of the window viewport.
1013 public int getOverdrawBottomHeightPix() { return mOverdrawBottomHeightPix; }
1016 * @return The amount to shrink the viewport relative to {@link #getViewportWidthPix()}.
1019 public int getViewportSizeOffsetWidthPix() { return mViewportSizeOffsetWidthPix; }
1022 * @return The amount to shrink the viewport relative to {@link #getViewportHeightPix()}.
1025 public int getViewportSizeOffsetHeightPix() { return mViewportSizeOffsetHeightPix; }
1028 * @see android.webkit.WebView#getContentHeight()
1030 public float getContentHeightCss() {
1031 return mRenderCoordinates.getContentHeightCss();
1035 * @see android.webkit.WebView#getContentWidth()
1037 public float getContentWidthCss() {
1038 return mRenderCoordinates.getContentWidthCss();
1041 public Bitmap getBitmap() {
1042 return getBitmap(getViewportWidthPix(), getViewportHeightPix());
1045 public Bitmap getBitmap(int width, int height) {
1046 if (width == 0 || height == 0
1047 || getViewportWidthPix() == 0 || getViewportHeightPix() == 0) {
1051 Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
1053 if (mNativeContentViewCore != 0 &&
1054 nativePopulateBitmapFromCompositor(mNativeContentViewCore, b)) {
1055 // If we successfully grabbed a bitmap, check if we have to draw the Android overlay
1056 // components as well.
1057 if (mContainerView.getChildCount() > 0) {
1058 Canvas c = new Canvas(b);
1059 c.scale(width / (float) getViewportWidthPix(),
1060 height / (float) getViewportHeightPix());
1061 mContainerView.draw(c);
1070 * Generates a bitmap of the content that is performance optimized based on capture time.
1073 * To have a consistent capture time across devices, we will scale down the captured bitmap
1074 * where necessary to reduce the time to generate the bitmap.
1076 * @param width The width of the content to be captured.
1077 * @param height The height of the content to be captured.
1078 * @return A pair of the generated bitmap, and the scale that needs to be applied to return the
1079 * bitmap to it's original size (i.e. if the bitmap is scaled down 50%, this
1082 public Pair<Bitmap, Float> getScaledPerformanceOptimizedBitmap(int width, int height) {
1084 // On tablets, always scale down to MDPI for performance reasons.
1085 if (DeviceUtils.isTablet(getContext())) {
1086 scale = getContext().getResources().getDisplayMetrics().density;
1089 getBitmap((int) (width / scale), (int) (height / scale)),
1093 // TODO(teddchoc): Remove all these navigation controller methods from here and have the
1094 // embedders manage it.
1096 * @return Whether the current WebContents has a previous navigation entry.
1098 public boolean canGoBack() {
1099 return mWebContents != null && mWebContents.getNavigationController().canGoBack();
1103 * @return Whether the current WebContents has a navigation entry after the current one.
1105 public boolean canGoForward() {
1106 return mWebContents != null && mWebContents.getNavigationController().canGoForward();
1110 * @param offset The offset into the navigation history.
1111 * @return Whether we can move in history by given offset
1113 public boolean canGoToOffset(int offset) {
1114 return mWebContents != null &&
1115 mWebContents.getNavigationController().canGoToOffset(offset);
1119 * Navigates to the specified offset from the "current entry". Does nothing if the offset is out
1121 * @param offset The offset into the navigation history.
1123 public void goToOffset(int offset) {
1124 if (mWebContents != null) mWebContents.getNavigationController().goToOffset(offset);
1128 public void goToNavigationIndex(int index) {
1129 if (mWebContents != null) {
1130 mWebContents.getNavigationController().goToNavigationIndex(index);
1135 * Goes to the navigation entry before the current one.
1137 public void goBack() {
1138 if (mWebContents != null) mWebContents.getNavigationController().goBack();
1142 * Goes to the navigation entry following the current one.
1144 public void goForward() {
1145 if (mWebContents != null) mWebContents.getNavigationController().goForward();
1149 * Loads the current navigation if there is a pending lazy load (after tab restore).
1151 public void loadIfNecessary() {
1152 if (mNativeContentViewCore != 0) nativeLoadIfNecessary(mNativeContentViewCore);
1156 * Requests the current navigation to be loaded upon the next call to loadIfNecessary().
1158 public void requestRestoreLoad() {
1159 if (mNativeContentViewCore != 0) nativeRequestRestoreLoad(mNativeContentViewCore);
1163 * Reload the current page.
1165 public void reload(boolean checkForRepost) {
1166 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1167 if (mNativeContentViewCore != 0) {
1168 nativeReload(mNativeContentViewCore, checkForRepost);
1173 * Reload the current page, ignoring the contents of the cache.
1175 public void reloadIgnoringCache(boolean checkForRepost) {
1176 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1177 if (mNativeContentViewCore != 0) {
1178 nativeReloadIgnoringCache(mNativeContentViewCore, checkForRepost);
1183 * Cancel the pending reload.
1185 public void cancelPendingReload() {
1186 if (mNativeContentViewCore != 0) nativeCancelPendingReload(mNativeContentViewCore);
1190 * Continue the pending reload.
1192 public void continuePendingReload() {
1193 if (mNativeContentViewCore != 0) nativeContinuePendingReload(mNativeContentViewCore);
1197 * Clears the ContentViewCore's page history in both the backwards and
1198 * forwards directions.
1200 public void clearHistory() {
1201 if (mNativeContentViewCore != 0) nativeClearHistory(mNativeContentViewCore);
1205 * @return The selected text (empty if no text selected).
1207 public String getSelectedText() {
1208 return mHasSelection ? mLastSelectedText : "";
1212 * @return Whether the current selection is editable (false if no text selected).
1214 public boolean isSelectionEditable() {
1215 return mHasSelection ? mSelectionEditable : false;
1218 // End FrameLayout overrides.
1221 * @see View#onTouchEvent(MotionEvent)
1223 public boolean onTouchEvent(MotionEvent event) {
1224 undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
1225 if (!mRequestedVSyncForInput) {
1226 mRequestedVSyncForInput = true;
1227 addVSyncSubscriber();
1229 return mContentViewGestureHandler.onTouchEvent(event);
1232 /** @see ContentViewGestureHandler#setIgnoreRemainingTouchEvents */
1233 public void setIgnoreRemainingTouchEvents() {
1234 mContentViewGestureHandler.setIgnoreRemainingTouchEvents();
1237 @SuppressWarnings("unused")
1239 private void onFlingStartEventConsumed(int vx, int vy) {
1240 temporarilyHideTextHandles();
1241 for (mGestureStateListenersIterator.rewind();
1242 mGestureStateListenersIterator.hasNext();) {
1243 mGestureStateListenersIterator.next().onFlingStartGesture(
1244 vx, vy, computeVerticalScrollOffset(), computeVerticalScrollExtent());
1248 @SuppressWarnings("unused")
1250 private void onFlingStartEventHadNoConsumer(int vx, int vy) {
1251 for (mGestureStateListenersIterator.rewind();
1252 mGestureStateListenersIterator.hasNext();) {
1253 mGestureStateListenersIterator.next().onUnhandledFlingStartEvent(vx, vy);
1257 @SuppressWarnings("unused")
1259 private void onFlingCancelEventAck() {
1260 updateGestureStateListener(GestureEventType.FLING_CANCEL);
1263 @SuppressWarnings("unused")
1265 private void onScrollBeginEventAck() {
1266 temporarilyHideTextHandles();
1267 mZoomControlsDelegate.invokeZoomPicker();
1268 updateGestureStateListener(GestureEventType.SCROLL_START);
1271 @SuppressWarnings("unused")
1273 private void onScrollUpdateGestureConsumed() {
1274 mZoomControlsDelegate.invokeZoomPicker();
1275 for (mGestureStateListenersIterator.rewind();
1276 mGestureStateListenersIterator.hasNext();) {
1277 mGestureStateListenersIterator.next().onScrollUpdateGestureConsumed();
1281 @SuppressWarnings("unused")
1283 private void onScrollEndEventAck() {
1284 updateGestureStateListener(GestureEventType.SCROLL_END);
1287 @SuppressWarnings("unused")
1289 private void onPinchBeginEventAck() {
1290 temporarilyHideTextHandles();
1291 updateGestureStateListener(GestureEventType.PINCH_BEGIN);
1294 @SuppressWarnings("unused")
1296 private void onPinchEndEventAck() {
1297 updateGestureStateListener(GestureEventType.PINCH_END);
1300 @SuppressWarnings("unused")
1302 private void onDoubleTapEventAck() {
1303 temporarilyHideTextHandles();
1307 * Called just prior to a tap or press gesture being forwarded to the renderer.
1309 @SuppressWarnings("unused")
1311 private boolean filterTapOrPressEvent(int type, int x, int y) {
1312 if (type == GestureEventType.LONG_PRESS && offerLongPressToEmbedder()) {
1315 updateForTapOrPress(type, x, y);
1316 updateForDoubleTapUMA(type);
1321 public void onTouchEventHandlingBegin(MotionEvent event) {
1322 if (mNativeContentViewCore == 0) return;
1323 nativeOnTouchEventHandlingBegin(mNativeContentViewCore,event);
1327 public void onTouchEventHandlingEnd() {
1328 if (mNativeContentViewCore == 0) return;
1329 nativeOnTouchEventHandlingEnd(mNativeContentViewCore);
1333 * Note: These events may or may not actually be forwarded to the renderer,
1334 * depending on ack disposition of the underlying touch events. All listening
1335 * for sent gestures should take place in {@link #filterGestureEvent(int, int, int)}.
1338 public boolean onGestureEventCreated(int type, long timeMs, int x, int y, Bundle b) {
1339 if (mNativeContentViewCore == 0) return false;
1341 case GestureEventType.SHOW_PRESS:
1342 nativeShowPress(mNativeContentViewCore, timeMs, x, y);
1344 case GestureEventType.TAP_CANCEL:
1345 nativeTapCancel(mNativeContentViewCore, timeMs, x, y);
1347 case GestureEventType.TAP_DOWN:
1348 nativeTapDown(mNativeContentViewCore, timeMs, x, y);
1350 case GestureEventType.DOUBLE_TAP:
1351 nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1353 case GestureEventType.SINGLE_TAP_UP:
1354 nativeSingleTap(mNativeContentViewCore, timeMs, x, y, false);
1356 case GestureEventType.SINGLE_TAP_CONFIRMED:
1357 if (!b.getBoolean(ContentViewGestureHandler.SHOW_PRESS, false)) {
1358 nativeShowPress(mNativeContentViewCore, timeMs, x, y);
1360 nativeSingleTap(mNativeContentViewCore, timeMs, x, y, false);
1362 case GestureEventType.SINGLE_TAP_UNCONFIRMED:
1363 nativeSingleTapUnconfirmed(mNativeContentViewCore, timeMs, x, y);
1365 case GestureEventType.LONG_PRESS:
1366 nativeLongPress(mNativeContentViewCore, timeMs, x, y, false);
1368 case GestureEventType.LONG_TAP:
1369 nativeLongTap(mNativeContentViewCore, timeMs, x, y, false);
1371 case GestureEventType.SCROLL_START: {
1372 int dx = b.getInt(ContentViewGestureHandler.DELTA_HINT_X);
1373 int dy = b.getInt(ContentViewGestureHandler.DELTA_HINT_Y);
1374 nativeScrollBegin(mNativeContentViewCore, timeMs, x, y, dx, dy);
1377 case GestureEventType.SCROLL_BY: {
1378 int dx = b.getInt(ContentViewGestureHandler.DISTANCE_X);
1379 int dy = b.getInt(ContentViewGestureHandler.DISTANCE_Y);
1380 nativeScrollBy(mNativeContentViewCore, timeMs, x, y, dx, dy);
1383 case GestureEventType.SCROLL_END:
1384 nativeScrollEnd(mNativeContentViewCore, timeMs);
1386 case GestureEventType.FLING_START:
1387 nativeFlingStart(mNativeContentViewCore, timeMs, x, y,
1388 b.getInt(ContentViewGestureHandler.VELOCITY_X, 0),
1389 b.getInt(ContentViewGestureHandler.VELOCITY_Y, 0));
1391 case GestureEventType.FLING_CANCEL:
1392 nativeFlingCancel(mNativeContentViewCore, timeMs);
1394 case GestureEventType.PINCH_BEGIN:
1395 nativePinchBegin(mNativeContentViewCore, timeMs, x, y);
1397 case GestureEventType.PINCH_BY:
1398 nativePinchBy(mNativeContentViewCore, timeMs, x, y,
1399 b.getFloat(ContentViewGestureHandler.DELTA, 0));
1401 case GestureEventType.PINCH_END:
1402 nativePinchEnd(mNativeContentViewCore, timeMs);
1410 public void sendDoubleTapForTest(long timeMs, int x, int y) {
1411 if (mNativeContentViewCore == 0) return;
1412 nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1416 public void flingForTest(long timeMs, int x, int y, int velocityX, int velocityY) {
1417 if (mNativeContentViewCore == 0) return;
1418 nativeFlingCancel(mNativeContentViewCore, timeMs);
1419 nativeScrollBegin(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1420 nativeFlingStart(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1424 * Add a listener that gets alerted on gesture state changes.
1425 * @param listener Listener to add.
1427 public void addGestureStateListener(GestureStateListener listener) {
1428 mGestureStateListeners.addObserver(listener);
1432 * Removes a listener that was added to watch for gesture state changes.
1433 * @param listener Listener to remove.
1435 public void removeGestureStateListener(GestureStateListener listener) {
1436 mGestureStateListeners.removeObserver(listener);
1439 void updateGestureStateListener(int gestureType) {
1440 for (mGestureStateListenersIterator.rewind();
1441 mGestureStateListenersIterator.hasNext();) {
1442 GestureStateListener listener = mGestureStateListenersIterator.next();
1443 switch (gestureType) {
1444 case GestureEventType.PINCH_BEGIN:
1445 listener.onPinchStarted();
1447 case GestureEventType.PINCH_END:
1448 listener.onPinchEnded();
1450 case GestureEventType.FLING_END:
1451 listener.onFlingEndGesture(
1452 computeVerticalScrollOffset(),
1453 computeVerticalScrollExtent());
1455 case GestureEventType.FLING_CANCEL:
1456 listener.onFlingCancelGesture();
1458 case GestureEventType.SCROLL_START:
1459 listener.onScrollStarted(
1460 computeVerticalScrollOffset(),
1461 computeVerticalScrollExtent());
1463 case GestureEventType.SCROLL_END:
1464 listener.onScrollEnded(
1465 computeVerticalScrollOffset(),
1466 computeVerticalScrollExtent());
1474 /** Callback interface for evaluateJavaScript(). */
1475 public interface JavaScriptCallback {
1476 void handleJavaScriptResult(String jsonResult);
1480 * Injects the passed Javascript code in the current page and evaluates it.
1481 * If a result is required, pass in a callback.
1482 * Used in automation tests.
1484 * @param script The Javascript to execute.
1485 * @param callback The callback to be fired off when a result is ready. The script's
1486 * result will be json encoded and passed as the parameter, and the call
1487 * will be made on the main thread.
1488 * If no result is required, pass null.
1490 public void evaluateJavaScript(String script, JavaScriptCallback callback) {
1491 if (mNativeContentViewCore == 0) return;
1492 nativeEvaluateJavaScript(mNativeContentViewCore, script, callback, false);
1496 * Injects the passed Javascript code in the current page and evaluates it.
1497 * If there is no page existing, a new one will be created.
1499 * @param script The Javascript to execute.
1501 public void evaluateJavaScriptEvenIfNotYetNavigated(String script) {
1502 if (mNativeContentViewCore == 0) return;
1503 nativeEvaluateJavaScript(mNativeContentViewCore, script, null, true);
1507 * To be called when the ContentView is shown.
1509 public void onShow() {
1510 assert mNativeContentViewCore != 0;
1511 if (!mInForeground) {
1512 int pid = nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1513 ChildProcessLauncher.getBindingManager().setInForeground(pid, true);
1515 mInForeground = true;
1516 nativeOnShow(mNativeContentViewCore);
1517 setAccessibilityState(mAccessibilityManager.isEnabled());
1521 * To be called when the ContentView is hidden.
1523 public void onHide() {
1524 assert mNativeContentViewCore != 0;
1525 if (mInForeground) {
1526 int pid = nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1527 ChildProcessLauncher.getBindingManager().setInForeground(pid, false);
1529 mInForeground = false;
1531 setInjectedAccessibility(false);
1532 nativeOnHide(mNativeContentViewCore);
1536 * Return the ContentSettings object used to retrieve the settings for this
1537 * ContentViewCore. For modifications, ChromeNativePreferences is to be used.
1538 * @return A ContentSettings object that can be used to retrieve this
1539 * ContentViewCore's settings.
1541 public ContentSettings getContentSettings() {
1542 return mContentSettings;
1545 private void onRenderCoordinatesUpdated() {
1546 if (mContentViewGestureHandler == null) return;
1548 // We disable double tap zoom for pages that have a width=device-width
1549 // or narrower viewport (indicating that this is a mobile-optimized or
1550 // responsive web design, so text will be legible without zooming).
1551 // We also disable it for pages that disallow the user from zooming in
1552 // or out (even if they don't have a device-width or narrower viewport).
1553 mContentViewGestureHandler.updateShouldDisableDoubleTap(
1554 mRenderCoordinates.hasMobileViewport() || mRenderCoordinates.hasFixedPageScale());
1557 private void hidePopupDialog() {
1558 SelectPopupDialog.hide(this);
1560 hideSelectActionBar();
1563 void hideSelectActionBar() {
1564 if (mActionMode != null) {
1565 mActionMode.finish();
1570 public boolean isSelectActionBarShowing() {
1571 return mActionMode != null;
1574 private void resetGestureDetectors() {
1575 mContentViewGestureHandler.resetGestureHandlers();
1579 * @see View#onAttachedToWindow()
1581 @SuppressWarnings("javadoc")
1582 public void onAttachedToWindow() {
1583 setAccessibilityState(mAccessibilityManager.isEnabled());
1587 * @see View#onDetachedFromWindow()
1589 @SuppressWarnings("javadoc")
1590 public void onDetachedFromWindow() {
1591 setInjectedAccessibility(false);
1593 mZoomControlsDelegate.dismissZoomPicker();
1594 unregisterAccessibilityContentObserver();
1598 * @see View#onVisibilityChanged(android.view.View, int)
1600 public void onVisibilityChanged(View changedView, int visibility) {
1601 if (visibility != View.VISIBLE) {
1602 mZoomControlsDelegate.dismissZoomPicker();
1607 * @see View#onCreateInputConnection(EditorInfo)
1609 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1610 if (!mImeAdapter.hasTextInputType()) {
1611 // Although onCheckIsTextEditor will return false in this case, the EditorInfo
1612 // is still used by the InputMethodService. Need to make sure the IME doesn't
1613 // enter fullscreen mode.
1614 outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
1617 mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter, outAttrs);
1618 return mInputConnection;
1621 public Editable getEditableForTest() {
1622 return mInputConnection.getEditable();
1626 * @see View#onCheckIsTextEditor()
1628 public boolean onCheckIsTextEditor() {
1629 return mImeAdapter.hasTextInputType();
1633 * @see View#onConfigurationChanged(Configuration)
1635 @SuppressWarnings("javadoc")
1636 public void onConfigurationChanged(Configuration newConfig) {
1639 if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
1640 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
1641 ImeAdapter.getTextInputTypeNone(),
1642 AdapterInputConnection.INVALID_SELECTION,
1643 AdapterInputConnection.INVALID_SELECTION);
1644 InputMethodManager manager = (InputMethodManager)
1645 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
1646 manager.restartInput(mContainerView);
1648 mContainerViewInternals.super_onConfigurationChanged(newConfig);
1649 // Make sure the size is up to date in JavaScript's window.onorientationchanged.
1650 mContainerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
1652 public void onLayoutChange(View v, int left, int top, int right, int bottom,
1653 int oldLeft, int oldTop, int oldRight, int oldBottom) {
1654 mContainerView.removeOnLayoutChangeListener(this);
1655 sendOrientationChangeEvent();
1658 // To request layout has side effect, but it seems OK as it only happen in
1659 // onConfigurationChange and layout has to be changed in most case.
1660 mContainerView.requestLayout();
1665 * @see View#onSizeChanged(int, int, int, int)
1667 @SuppressWarnings("javadoc")
1668 public void onSizeChanged(int wPix, int hPix, int owPix, int ohPix) {
1669 if (getViewportWidthPix() == wPix && getViewportHeightPix() == hPix) return;
1671 mViewportWidthPix = wPix;
1672 mViewportHeightPix = hPix;
1673 if (mNativeContentViewCore != 0) {
1674 nativeWasResized(mNativeContentViewCore);
1677 updateAfterSizeChanged();
1681 * Called when the ContentView's position in the activity window changed. This information is
1682 * used for cropping screenshots.
1684 public void onLocationInWindowChanged(int x, int y) {
1685 mLocationInWindowX = x;
1686 mLocationInWindowY = y;
1690 * Called when the underlying surface the compositor draws to changes size.
1691 * This may be larger than the viewport size.
1693 public void onPhysicalBackingSizeChanged(int wPix, int hPix) {
1694 if (mPhysicalBackingWidthPix == wPix && mPhysicalBackingHeightPix == hPix) return;
1696 mPhysicalBackingWidthPix = wPix;
1697 mPhysicalBackingHeightPix = hPix;
1699 if (mNativeContentViewCore != 0) {
1700 nativeWasResized(mNativeContentViewCore);
1705 * Called when the amount the surface is overdrawing off the bottom has changed.
1706 * @param overdrawHeightPix The overdraw height.
1708 public void onOverdrawBottomHeightChanged(int overdrawHeightPix) {
1709 if (mOverdrawBottomHeightPix == overdrawHeightPix) return;
1711 mOverdrawBottomHeightPix = overdrawHeightPix;
1713 if (mNativeContentViewCore != 0) {
1714 nativeWasResized(mNativeContentViewCore);
1718 private void updateAfterSizeChanged() {
1719 mPopupZoomer.hide(false);
1721 // Execute a delayed form focus operation because the OSK was brought
1723 if (!mFocusPreOSKViewportRect.isEmpty()) {
1724 Rect rect = new Rect();
1725 getContainerView().getWindowVisibleDisplayFrame(rect);
1726 if (!rect.equals(mFocusPreOSKViewportRect)) {
1727 // Only assume the OSK triggered the onSizeChanged if width was preserved.
1728 if (rect.width() == mFocusPreOSKViewportRect.width()) {
1729 scrollFocusedEditableNodeIntoView();
1731 mFocusPreOSKViewportRect.setEmpty();
1733 } else if (mUnfocusOnNextSizeChanged) {
1734 undoScrollFocusedEditableNodeIntoViewIfNeeded(true);
1735 mUnfocusOnNextSizeChanged = false;
1739 private void scrollFocusedEditableNodeIntoView() {
1740 if (mNativeContentViewCore != 0) {
1741 Runnable scrollTask = new Runnable() {
1744 if (mNativeContentViewCore != 0) {
1745 nativeScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1752 // The native side keeps track of whether the zoom and scroll actually occurred. It is
1753 // more efficient to do it this way and sometimes fire an unnecessary message rather
1754 // than synchronize with the renderer and always have an additional message.
1755 mScrolledAndZoomedFocusedEditableNode = true;
1759 private void undoScrollFocusedEditableNodeIntoViewIfNeeded(boolean backButtonPressed) {
1760 // The only call to this function that matters is the first call after the
1761 // scrollFocusedEditableNodeIntoView function call.
1762 // If the first call to this function is a result of a back button press we want to undo the
1763 // preceding scroll. If the call is a result of some other action we don't want to perform
1765 // All subsequent calls are ignored since only the scroll function sets
1766 // mScrolledAndZoomedFocusedEditableNode to true.
1767 if (mScrolledAndZoomedFocusedEditableNode && backButtonPressed &&
1768 mNativeContentViewCore != 0) {
1769 Runnable scrollTask = new Runnable() {
1772 if (mNativeContentViewCore != 0) {
1773 nativeUndoScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1780 mScrolledAndZoomedFocusedEditableNode = false;
1784 * @see View#onWindowFocusChanged(boolean)
1786 public void onWindowFocusChanged(boolean hasWindowFocus) {
1787 if (!hasWindowFocus) {
1788 mContentViewGestureHandler.onWindowFocusLost();
1792 public void onFocusChanged(boolean gainFocus) {
1793 if (!gainFocus) getContentViewClient().onImeStateChangeRequested(false);
1794 if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1798 * @see View#onKeyUp(int, KeyEvent)
1800 public boolean onKeyUp(int keyCode, KeyEvent event) {
1801 if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1802 mPopupZoomer.hide(true);
1805 return mContainerViewInternals.super_onKeyUp(keyCode, event);
1809 * @see View#dispatchKeyEventPreIme(KeyEvent)
1811 public boolean dispatchKeyEventPreIme(KeyEvent event) {
1814 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && mImeAdapter.isActive()) {
1815 mUnfocusOnNextSizeChanged = true;
1817 undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
1819 return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1826 * @see View#dispatchKeyEvent(KeyEvent)
1828 public boolean dispatchKeyEvent(KeyEvent event) {
1829 if (getContentViewClient().shouldOverrideKeyEvent(event)) {
1830 return mContainerViewInternals.super_dispatchKeyEvent(event);
1833 if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
1835 // Event is not consumed here, because ImeAdapter might interpret
1837 // showImeIfNeeded respects the policy of
1838 // InputMethodService.onEvaluateInputViewShown. So IME will not be
1839 // shown if you have QWERTY physical keyboard attached.
1840 // Also, IME will not be shown if the focus is not on the input
1841 // field. See ImeAdapter.attachAndShowIfNeeded
1844 if (mImeAdapter.dispatchKeyEvent(event)) return true;
1846 return mContainerViewInternals.super_dispatchKeyEvent(event);
1850 * @see View#onHoverEvent(MotionEvent)
1851 * Mouse move events are sent on hover enter, hover move and hover exit.
1852 * They are sent on hover exit because sometimes it acts as both a hover
1853 * move and hover exit.
1855 public boolean onHoverEvent(MotionEvent event) {
1856 TraceEvent.begin("onHoverEvent");
1857 mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1858 if (mBrowserAccessibilityManager != null) {
1859 return mBrowserAccessibilityManager.onHoverEvent(event);
1861 if (mNativeContentViewCore != 0) {
1862 nativeSendMouseMoveEvent(mNativeContentViewCore, event.getEventTime(),
1863 event.getX(), event.getY());
1865 TraceEvent.end("onHoverEvent");
1870 * @see View#onGenericMotionEvent(MotionEvent)
1872 public boolean onGenericMotionEvent(MotionEvent event) {
1873 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1874 switch (event.getAction()) {
1875 case MotionEvent.ACTION_SCROLL:
1876 nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
1877 event.getX(), event.getY(),
1878 event.getAxisValue(MotionEvent.AXIS_VSCROLL));
1880 mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1881 // Send a delayed onMouseMove event so that we end
1882 // up hovering over the right position after the scroll.
1883 final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event);
1884 mFakeMouseMoveRunnable = new Runnable() {
1887 onHoverEvent(eventFakeMouseMove);
1890 mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1894 return mContainerViewInternals.super_onGenericMotionEvent(event);
1898 * @see View#scrollBy(int, int)
1899 * Currently the ContentView scrolling happens in the native side. In
1900 * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
1901 * are overridden, so that View's mScrollX and mScrollY will be unchanged at
1902 * (0, 0). This is critical for drawing ContentView correctly.
1904 public void scrollBy(int xPix, int yPix) {
1905 if (mNativeContentViewCore != 0) {
1906 nativeScrollBy(mNativeContentViewCore,
1907 System.currentTimeMillis(), 0, 0, xPix, yPix);
1912 * @see View#scrollTo(int, int)
1914 public void scrollTo(int xPix, int yPix) {
1915 if (mNativeContentViewCore == 0) return;
1916 final float xCurrentPix = mRenderCoordinates.getScrollXPix();
1917 final float yCurrentPix = mRenderCoordinates.getScrollYPix();
1918 final float dxPix = xPix - xCurrentPix;
1919 final float dyPix = yPix - yCurrentPix;
1920 if (dxPix != 0 || dyPix != 0) {
1921 long time = System.currentTimeMillis();
1922 nativeScrollBegin(mNativeContentViewCore, time,
1923 xCurrentPix, yCurrentPix, -dxPix, -dyPix);
1924 nativeScrollBy(mNativeContentViewCore,
1925 time, xCurrentPix, yCurrentPix, dxPix, dyPix);
1926 nativeScrollEnd(mNativeContentViewCore, time);
1930 // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1932 public int getNativeScrollXForTest() {
1933 return mRenderCoordinates.getScrollXPixInt();
1936 // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1938 public int getNativeScrollYForTest() {
1939 return mRenderCoordinates.getScrollYPixInt();
1943 * @see View#computeHorizontalScrollExtent()
1945 @SuppressWarnings("javadoc")
1946 public int computeHorizontalScrollExtent() {
1947 return mRenderCoordinates.getLastFrameViewportWidthPixInt();
1951 * @see View#computeHorizontalScrollOffset()
1953 @SuppressWarnings("javadoc")
1954 public int computeHorizontalScrollOffset() {
1955 return mRenderCoordinates.getScrollXPixInt();
1959 * @see View#computeHorizontalScrollRange()
1961 @SuppressWarnings("javadoc")
1962 public int computeHorizontalScrollRange() {
1963 return mRenderCoordinates.getContentWidthPixInt();
1967 * @see View#computeVerticalScrollExtent()
1969 @SuppressWarnings("javadoc")
1970 public int computeVerticalScrollExtent() {
1971 return mRenderCoordinates.getLastFrameViewportHeightPixInt();
1975 * @see View#computeVerticalScrollOffset()
1977 @SuppressWarnings("javadoc")
1978 public int computeVerticalScrollOffset() {
1979 return mRenderCoordinates.getScrollYPixInt();
1983 * @see View#computeVerticalScrollRange()
1985 @SuppressWarnings("javadoc")
1986 public int computeVerticalScrollRange() {
1987 return mRenderCoordinates.getContentHeightPixInt();
1990 // End FrameLayout overrides.
1993 * @see View#awakenScrollBars(int, boolean)
1995 @SuppressWarnings("javadoc")
1996 public boolean awakenScrollBars(int startDelay, boolean invalidate) {
1997 // For the default implementation of ContentView which draws the scrollBars on the native
1998 // side, calling this function may get us into a bad state where we keep drawing the
1999 // scrollBars, so disable it by always returning false.
2000 if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
2003 return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
2007 private void updateForTapOrPress(int type, float xPix, float yPix) {
2008 if (type != GestureEventType.SINGLE_TAP_CONFIRMED
2009 && type != GestureEventType.SINGLE_TAP_UP
2010 && type != GestureEventType.LONG_PRESS
2011 && type != GestureEventType.LONG_TAP) {
2015 if (mContainerView.isFocusable() && mContainerView.isFocusableInTouchMode()
2016 && !mContainerView.isFocused()) {
2017 mContainerView.requestFocus();
2020 if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
2022 if (type == GestureEventType.LONG_PRESS
2023 || type == GestureEventType.LONG_TAP) {
2024 getInsertionHandleController().allowAutomaticShowing();
2025 getSelectionHandleController().allowAutomaticShowing();
2027 setClickXAndY((int) xPix, (int) yPix);
2028 if (mSelectionEditable) getInsertionHandleController().allowAutomaticShowing();
2032 private void setClickXAndY(int x, int y) {
2038 * @return The x coordinate for the last point that a singleTap gesture was initiated from.
2040 public int getSingleTapX() {
2045 * @return The y coordinate for the last point that a singleTap gesture was initiated from.
2047 public int getSingleTapY() {
2051 // Watch for the UMA "action after double tap" timer expiring and reset
2052 // the timer if necessary.
2053 private void updateDoubleTapUmaTimer() {
2054 if (mLastDoubleTapTimeMs == 0) return;
2056 long nowMs = SystemClock.uptimeMillis();
2057 if ((nowMs - mLastDoubleTapTimeMs) >= ACTION_AFTER_DOUBLE_TAP_WINDOW_MS) {
2058 // Time expired, user took no action (that we care about).
2059 sendActionAfterDoubleTapUMA(UMAActionAfterDoubleTap.NO_ACTION);
2060 mLastDoubleTapTimeMs = 0;
2064 private void updateForDoubleTapUMA(int type) {
2065 updateDoubleTapUmaTimer();
2067 if (type == GestureEventType.SINGLE_TAP_UP
2068 || type == GestureEventType.SINGLE_TAP_CONFIRMED) {
2069 sendSingleTapUMA(mContentViewGestureHandler.isDoubleTapDisabled() ?
2070 UMASingleTapType.UNDELAYED_TAP : UMASingleTapType.DELAYED_TAP);
2071 } else if (type == GestureEventType.DOUBLE_TAP) {
2072 // Make sure repeated double taps don't get silently dropped from
2074 if (mLastDoubleTapTimeMs > 0) {
2075 sendActionAfterDoubleTapUMA(UMAActionAfterDoubleTap.NO_ACTION);
2078 mLastDoubleTapTimeMs = SystemClock.uptimeMillis();
2082 private void reportActionAfterDoubleTapUMA(int type) {
2083 updateDoubleTapUmaTimer();
2085 if (mLastDoubleTapTimeMs == 0) return;
2087 long nowMs = SystemClock.uptimeMillis();
2088 if ((nowMs - mLastDoubleTapTimeMs) < ACTION_AFTER_DOUBLE_TAP_WINDOW_MS) {
2089 sendActionAfterDoubleTapUMA(type);
2090 mLastDoubleTapTimeMs = 0;
2094 private void sendSingleTapUMA(int type) {
2095 if (mNativeContentViewCore == 0) return;
2096 nativeSendSingleTapUma(
2097 mNativeContentViewCore,
2099 UMASingleTapType.COUNT);
2102 private void sendActionAfterDoubleTapUMA(int type) {
2103 if (mNativeContentViewCore == 0) return;
2104 nativeSendActionAfterDoubleTapUma(
2105 mNativeContentViewCore,
2107 !mContentViewGestureHandler.isClickDelayDisabled(),
2108 UMAActionAfterDoubleTap.COUNT);
2111 public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
2112 mZoomControlsDelegate = zoomControlsDelegate;
2115 public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
2116 mContentViewGestureHandler.updateMultiTouchSupport(supportsMultiTouchZoom);
2119 public void updateDoubleTapSupport(boolean supportsDoubleTap) {
2120 mContentViewGestureHandler.updateDoubleTapSupport(supportsDoubleTap);
2123 public void selectPopupMenuItems(int[] indices) {
2124 if (mNativeContentViewCore != 0) {
2125 nativeSelectPopupMenuItems(mNativeContentViewCore, indices);
2130 * Get the screen orientation from the OS and push it to WebKit.
2132 * TODO(husky): Add a hook for mock orientations.
2134 private void sendOrientationChangeEvent() {
2135 if (mNativeContentViewCore == 0) return;
2137 WindowManager windowManager =
2138 (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
2139 switch (windowManager.getDefaultDisplay().getRotation()) {
2140 case Surface.ROTATION_90:
2141 nativeSendOrientationChangeEvent(mNativeContentViewCore, 90);
2143 case Surface.ROTATION_180:
2144 nativeSendOrientationChangeEvent(mNativeContentViewCore, 180);
2146 case Surface.ROTATION_270:
2147 nativeSendOrientationChangeEvent(mNativeContentViewCore, -90);
2149 case Surface.ROTATION_0:
2150 nativeSendOrientationChangeEvent(mNativeContentViewCore, 0);
2153 Log.w(TAG, "Unknown rotation!");
2159 * Register the delegate to be used when content can not be handled by
2160 * the rendering engine, and should be downloaded instead. This will replace
2161 * the current delegate, if any.
2162 * @param delegate An implementation of ContentViewDownloadDelegate.
2164 public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
2165 mDownloadDelegate = delegate;
2168 // Called by DownloadController.
2169 ContentViewDownloadDelegate getDownloadDelegate() {
2170 return mDownloadDelegate;
2173 private SelectionHandleController getSelectionHandleController() {
2174 if (mSelectionHandleController == null) {
2175 mSelectionHandleController = new SelectionHandleController(
2176 getContainerView(), mPositionObserver) {
2178 public void selectBetweenCoordinates(int x1, int y1, int x2, int y2) {
2179 if (mNativeContentViewCore != 0 && !(x1 == x2 && y1 == y2)) {
2180 nativeSelectBetweenCoordinates(mNativeContentViewCore,
2181 x1, y1 - mRenderCoordinates.getContentOffsetYPix(),
2182 x2, y2 - mRenderCoordinates.getContentOffsetYPix());
2187 public void showHandles(int startDir, int endDir) {
2188 super.showHandles(startDir, endDir);
2189 showSelectActionBar();
2194 mSelectionHandleController.hideAndDisallowAutomaticShowing();
2197 return mSelectionHandleController;
2200 private InsertionHandleController getInsertionHandleController() {
2201 if (mInsertionHandleController == null) {
2202 mInsertionHandleController = new InsertionHandleController(
2203 getContainerView(), mPositionObserver) {
2204 private static final int AVERAGE_LINE_HEIGHT = 14;
2207 public void setCursorPosition(int x, int y) {
2208 if (mNativeContentViewCore != 0) {
2209 nativeMoveCaret(mNativeContentViewCore,
2210 x, y - mRenderCoordinates.getContentOffsetYPix());
2215 public void paste() {
2216 mImeAdapter.paste();
2221 public int getLineHeight() {
2222 return (int) Math.ceil(
2223 mRenderCoordinates.fromLocalCssToPix(AVERAGE_LINE_HEIGHT));
2227 public void showHandle() {
2232 mInsertionHandleController.hideAndDisallowAutomaticShowing();
2235 return mInsertionHandleController;
2239 public InsertionHandleController getInsertionHandleControllerForTest() {
2240 return mInsertionHandleController;
2244 public SelectionHandleController getSelectionHandleControllerForTest() {
2245 return mSelectionHandleController;
2248 private void updateHandleScreenPositions() {
2249 if (isSelectionHandleShowing()) {
2250 mSelectionHandleController.setStartHandlePosition(
2251 mStartHandlePoint.getXPix(), mStartHandlePoint.getYPix());
2252 mSelectionHandleController.setEndHandlePosition(
2253 mEndHandlePoint.getXPix(), mEndHandlePoint.getYPix());
2256 if (isInsertionHandleShowing()) {
2257 mInsertionHandleController.setHandlePosition(
2258 mInsertionHandlePoint.getXPix(), mInsertionHandlePoint.getYPix());
2262 private void hideHandles() {
2263 if (mSelectionHandleController != null) {
2264 mSelectionHandleController.hideAndDisallowAutomaticShowing();
2266 if (mInsertionHandleController != null) {
2267 mInsertionHandleController.hideAndDisallowAutomaticShowing();
2269 mPositionObserver.removeListener(mPositionListener);
2272 private void showSelectActionBar() {
2273 if (mActionMode != null) {
2274 mActionMode.invalidate();
2278 // Start a new action mode with a SelectActionModeCallback.
2279 SelectActionModeCallback.ActionHandler actionHandler =
2280 new SelectActionModeCallback.ActionHandler() {
2282 public void selectAll() {
2283 mImeAdapter.selectAll();
2292 public void copy() {
2297 public void paste() {
2298 mImeAdapter.paste();
2302 public void share() {
2303 final String query = getSelectedText();
2304 if (TextUtils.isEmpty(query)) return;
2306 Intent send = new Intent(Intent.ACTION_SEND);
2307 send.setType("text/plain");
2308 send.putExtra(Intent.EXTRA_TEXT, query);
2310 Intent i = Intent.createChooser(send, getContext().getString(
2311 R.string.actionbar_share));
2312 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2313 getContext().startActivity(i);
2314 } catch (android.content.ActivityNotFoundException ex) {
2315 // If no app handles it, do nothing.
2320 public void search() {
2321 final String query = getSelectedText();
2322 if (TextUtils.isEmpty(query)) return;
2324 // See if ContentViewClient wants to override
2325 if (getContentViewClient().doesPerformWebSearch()) {
2326 getContentViewClient().performWebSearch(query);
2330 Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
2331 i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2332 i.putExtra(SearchManager.QUERY, query);
2333 i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
2334 if (!(getContext() instanceof Activity)) {
2335 i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2338 getContext().startActivity(i);
2339 } catch (android.content.ActivityNotFoundException ex) {
2340 // If no app handles it, do nothing.
2345 public boolean isSelectionEditable() {
2346 return mSelectionEditable;
2350 public void onDestroyActionMode() {
2352 if (mUnselectAllOnActionModeDismiss) mImeAdapter.unselect();
2353 getContentViewClient().onContextualActionBarHidden();
2357 public boolean isShareAvailable() {
2358 Intent intent = new Intent(Intent.ACTION_SEND);
2359 intent.setType("text/plain");
2360 return getContext().getPackageManager().queryIntentActivities(intent,
2361 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2365 public boolean isWebSearchAvailable() {
2366 if (getContentViewClient().doesPerformWebSearch()) return true;
2367 Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
2368 intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
2369 return getContext().getPackageManager().queryIntentActivities(intent,
2370 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
2374 // On ICS, startActionMode throws an NPE when getParent() is null.
2375 if (mContainerView.getParent() != null) {
2376 mActionMode = mContainerView.startActionMode(
2377 getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler,
2378 nativeIsIncognito(mNativeContentViewCore)));
2380 mUnselectAllOnActionModeDismiss = true;
2381 if (mActionMode == null) {
2382 // There is no ActionMode, so remove the selection.
2383 mImeAdapter.unselect();
2385 getContentViewClient().onContextualActionBarShown();
2389 public boolean getUseDesktopUserAgent() {
2390 if (mNativeContentViewCore != 0) {
2391 return nativeGetUseDesktopUserAgent(mNativeContentViewCore);
2397 * Set whether or not we're using a desktop user agent for the currently loaded page.
2398 * @param override If true, use a desktop user agent. Use a mobile one otherwise.
2399 * @param reloadOnChange Reload the page if the UA has changed.
2401 public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange) {
2402 if (mNativeContentViewCore != 0) {
2403 nativeSetUseDesktopUserAgent(mNativeContentViewCore, override, reloadOnChange);
2407 public void clearSslPreferences() {
2408 nativeClearSslPreferences(mNativeContentViewCore);
2411 private boolean isSelectionHandleShowing() {
2412 return mSelectionHandleController != null && mSelectionHandleController.isShowing();
2415 private boolean isInsertionHandleShowing() {
2416 return mInsertionHandleController != null && mInsertionHandleController.isShowing();
2419 // Makes the insertion/selection handles invisible. They will fade back in shortly after the
2420 // last call to scheduleTextHandleFadeIn (or temporarilyHideTextHandles).
2421 private void temporarilyHideTextHandles() {
2422 if (isSelectionHandleShowing() && !mSelectionHandleController.isDragging()) {
2423 mSelectionHandleController.setHandleVisibility(HandleView.INVISIBLE);
2425 if (isInsertionHandleShowing() && !mInsertionHandleController.isDragging()) {
2426 mInsertionHandleController.setHandleVisibility(HandleView.INVISIBLE);
2428 scheduleTextHandleFadeIn();
2431 private boolean allowTextHandleFadeIn() {
2432 if (mContentViewGestureHandler.isNativeScrolling() ||
2433 mContentViewGestureHandler.isNativePinching()) {
2437 if (mPopupZoomer.isShowing()) return false;
2442 // Cancels any pending fade in and schedules a new one.
2443 private void scheduleTextHandleFadeIn() {
2444 if (!isInsertionHandleShowing() && !isSelectionHandleShowing()) return;
2446 if (mDeferredHandleFadeInRunnable == null) {
2447 mDeferredHandleFadeInRunnable = new Runnable() {
2450 if (!allowTextHandleFadeIn()) {
2451 // Delay fade in until it is allowed.
2452 scheduleTextHandleFadeIn();
2454 if (isSelectionHandleShowing()) {
2455 mSelectionHandleController.beginHandleFadeIn();
2457 if (isInsertionHandleShowing()) {
2458 mInsertionHandleController.beginHandleFadeIn();
2465 mContainerView.removeCallbacks(mDeferredHandleFadeInRunnable);
2466 mContainerView.postDelayed(mDeferredHandleFadeInRunnable, TEXT_HANDLE_FADE_IN_DELAY);
2470 * Shows the IME if the focused widget could accept text input.
2472 public void showImeIfNeeded() {
2473 if (mNativeContentViewCore != 0) nativeShowImeIfNeeded(mNativeContentViewCore);
2476 @SuppressWarnings("unused")
2478 private void updateFrameInfo(
2479 float scrollOffsetX, float scrollOffsetY,
2480 float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
2481 float contentWidth, float contentHeight,
2482 float viewportWidth, float viewportHeight,
2483 float controlsOffsetYCss, float contentOffsetYCss,
2484 float overdrawBottomHeightCss) {
2485 TraceEvent.instant("ContentViewCore:updateFrameInfo");
2486 // Adjust contentWidth/Height to be always at least as big as
2487 // the actual viewport (as set by onSizeChanged).
2488 contentWidth = Math.max(contentWidth,
2489 mRenderCoordinates.fromPixToLocalCss(mViewportWidthPix));
2490 contentHeight = Math.max(contentHeight,
2491 mRenderCoordinates.fromPixToLocalCss(mViewportHeightPix));
2493 final float contentOffsetYPix = mRenderCoordinates.fromDipToPix(contentOffsetYCss);
2495 final boolean contentSizeChanged =
2496 contentWidth != mRenderCoordinates.getContentWidthCss()
2497 || contentHeight != mRenderCoordinates.getContentHeightCss();
2498 final boolean scaleLimitsChanged =
2499 minPageScaleFactor != mRenderCoordinates.getMinPageScaleFactor()
2500 || maxPageScaleFactor != mRenderCoordinates.getMaxPageScaleFactor();
2501 final boolean pageScaleChanged =
2502 pageScaleFactor != mRenderCoordinates.getPageScaleFactor();
2503 final boolean scrollChanged =
2505 || scrollOffsetX != mRenderCoordinates.getScrollX()
2506 || scrollOffsetY != mRenderCoordinates.getScrollY();
2507 final boolean contentOffsetChanged =
2508 contentOffsetYPix != mRenderCoordinates.getContentOffsetYPix();
2510 final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
2511 final boolean needUpdateZoomControls = scaleLimitsChanged || scrollChanged;
2512 final boolean needTemporarilyHideHandles = scrollChanged;
2514 if (needHidePopupZoomer) mPopupZoomer.hide(true);
2516 if (scrollChanged) {
2517 mContainerViewInternals.onScrollChanged(
2518 (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetX),
2519 (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetY),
2520 (int) mRenderCoordinates.getScrollXPix(),
2521 (int) mRenderCoordinates.getScrollYPix());
2524 mRenderCoordinates.updateFrameInfo(
2525 scrollOffsetX, scrollOffsetY,
2526 contentWidth, contentHeight,
2527 viewportWidth, viewportHeight,
2528 pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
2530 onRenderCoordinatesUpdated();
2532 if (scrollChanged || contentOffsetChanged) {
2533 for (mGestureStateListenersIterator.rewind();
2534 mGestureStateListenersIterator.hasNext();) {
2535 mGestureStateListenersIterator.next().onScrollOffsetOrExtentChanged(
2536 computeVerticalScrollOffset(),
2537 computeVerticalScrollExtent());
2541 if (needTemporarilyHideHandles) temporarilyHideTextHandles();
2542 if (needUpdateZoomControls) mZoomControlsDelegate.updateZoomControls();
2543 if (contentOffsetChanged) updateHandleScreenPositions();
2545 // Update offsets for fullscreen.
2546 final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2547 final float controlsOffsetPix = controlsOffsetYCss * deviceScale;
2548 final float overdrawBottomHeightPix = overdrawBottomHeightCss * deviceScale;
2549 getContentViewClient().onOffsetsForFullscreenChanged(
2550 controlsOffsetPix, contentOffsetYPix, overdrawBottomHeightPix);
2552 mPendingRendererFrame = true;
2553 if (mBrowserAccessibilityManager != null) {
2554 mBrowserAccessibilityManager.notifyFrameInfoInitialized();
2557 // Update geometry for external video surface.
2558 getContentViewClient().onGeometryChanged(-1, null);
2562 private void updateImeAdapter(int nativeImeAdapterAndroid, int textInputType,
2563 String text, int selectionStart, int selectionEnd,
2564 int compositionStart, int compositionEnd, boolean showImeIfNeeded, boolean requireAck) {
2566 mSelectionEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
2568 if (mActionMode != null) mActionMode.invalidate();
2570 mImeAdapter.attachAndShowIfNeeded(nativeImeAdapterAndroid, textInputType,
2571 selectionStart, selectionEnd, showImeIfNeeded);
2573 if (mInputConnection != null) {
2574 mInputConnection.updateState(text, selectionStart, selectionEnd, compositionStart,
2575 compositionEnd, requireAck);
2580 @SuppressWarnings("unused")
2582 private void setTitle(String title) {
2583 getContentViewClient().onUpdateTitle(title);
2587 * Called (from native) when the <select> popup needs to be shown.
2588 * @param items Items to show.
2589 * @param enabled POPUP_ITEM_TYPEs for items.
2590 * @param multiple Whether the popup menu should support multi-select.
2591 * @param selectedIndices Indices of selected items.
2593 @SuppressWarnings("unused")
2595 private void showSelectPopup(String[] items, int[] enabled, boolean multiple,
2596 int[] selectedIndices) {
2597 assert items.length == enabled.length;
2598 List<SelectPopupItem> popupItems = new ArrayList<SelectPopupItem>();
2599 for (int i = 0; i < items.length; i++) {
2600 popupItems.add(new SelectPopupItem(items[i], enabled[i]));
2602 SelectPopupDialog.show(this, popupItems, multiple, selectedIndices);
2605 @SuppressWarnings("unused")
2607 private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
2608 mPopupZoomer.setBitmap(zoomedBitmap);
2609 mPopupZoomer.show(targetRect);
2610 temporarilyHideTextHandles();
2613 @SuppressWarnings("unused")
2615 private TouchEventSynthesizer createTouchEventSynthesizer() {
2616 return new TouchEventSynthesizer(this);
2619 @SuppressWarnings("unused")
2621 private void onSelectionChanged(String text) {
2622 mLastSelectedText = text;
2625 @SuppressWarnings("unused")
2627 private void onSelectionBoundsChanged(Rect anchorRectDip, int anchorDir, Rect focusRectDip,
2628 int focusDir, boolean isAnchorFirst) {
2629 // All coordinates are in DIP.
2630 int x1 = anchorRectDip.left;
2631 int y1 = anchorRectDip.bottom;
2632 int x2 = focusRectDip.left;
2633 int y2 = focusRectDip.bottom;
2635 if (x1 != x2 || y1 != y2 ||
2636 (mSelectionHandleController != null && mSelectionHandleController.isDragging())) {
2637 if (mInsertionHandleController != null) {
2638 mInsertionHandleController.hide();
2640 if (isAnchorFirst) {
2641 mStartHandlePoint.setLocalDip(x1, y1);
2642 mEndHandlePoint.setLocalDip(x2, y2);
2644 mStartHandlePoint.setLocalDip(x2, y2);
2645 mEndHandlePoint.setLocalDip(x1, y1);
2648 boolean wereSelectionHandlesShowing = getSelectionHandleController().isShowing();
2650 getSelectionHandleController().onSelectionChanged(anchorDir, focusDir);
2651 updateHandleScreenPositions();
2652 mHasSelection = true;
2654 if (!wereSelectionHandlesShowing && getSelectionHandleController().isShowing()) {
2655 // TODO(cjhopman): Remove this when there is a better signal that long press caused
2656 // a selection. See http://crbug.com/150151.
2657 mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2661 mUnselectAllOnActionModeDismiss = false;
2662 hideSelectActionBar();
2663 if (x1 != 0 && y1 != 0 && mSelectionEditable) {
2664 // Selection is a caret, and a text field is focused.
2665 if (mSelectionHandleController != null) {
2666 mSelectionHandleController.hide();
2668 mInsertionHandlePoint.setLocalDip(x1, y1);
2670 getInsertionHandleController().onCursorPositionChanged();
2671 updateHandleScreenPositions();
2672 InputMethodManager manager = (InputMethodManager)
2673 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
2674 if (manager.isWatchingCursor(mContainerView)) {
2675 final int xPix = (int) mInsertionHandlePoint.getXPix();
2676 final int yPix = (int) mInsertionHandlePoint.getYPix();
2677 manager.updateCursor(mContainerView, xPix, yPix, xPix, yPix);
2681 if (mSelectionHandleController != null) {
2682 mSelectionHandleController.hideAndDisallowAutomaticShowing();
2684 if (mInsertionHandleController != null) {
2685 mInsertionHandleController.hideAndDisallowAutomaticShowing();
2688 mHasSelection = false;
2690 if (isSelectionHandleShowing() || isInsertionHandleShowing()) {
2691 mPositionObserver.addListener(mPositionListener);
2695 @SuppressWarnings("unused")
2697 private static void onEvaluateJavaScriptResult(
2698 String jsonResult, JavaScriptCallback callback) {
2699 callback.handleJavaScriptResult(jsonResult);
2702 @SuppressWarnings("unused")
2704 private void showPastePopup(int xDip, int yDip) {
2705 mInsertionHandlePoint.setLocalDip(xDip, yDip);
2706 getInsertionHandleController().showHandle();
2707 updateHandleScreenPositions();
2708 getInsertionHandleController().showHandleWithPastePopup();
2711 @SuppressWarnings("unused")
2713 private void onRenderProcessSwap(int oldPid, int newPid) {
2714 if (!mInForeground) {
2715 ChildProcessLauncher.getBindingManager().setInForeground(newPid, false);
2716 } else if (oldPid != newPid) {
2717 ChildProcessLauncher.getBindingManager().setInForeground(oldPid, false);
2718 ChildProcessLauncher.getBindingManager().setInForeground(newPid, true);
2724 @SuppressWarnings("unused")
2726 private void onWebContentsConnected() {
2731 * Attaches the native ImeAdapter object to the java ImeAdapter to allow communication via JNI.
2733 public void attachImeAdapter() {
2734 if (mImeAdapter != null && mNativeContentViewCore != 0) {
2735 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2740 * @see View#hasFocus()
2743 public boolean hasFocus() {
2744 return mContainerView.hasFocus();
2748 * Checks whether the ContentViewCore can be zoomed in.
2750 * @return True if the ContentViewCore can be zoomed in.
2752 // This method uses the term 'zoom' for legacy reasons, but relates
2753 // to what chrome calls the 'page scale factor'.
2754 public boolean canZoomIn() {
2755 final float zoomInExtent = mRenderCoordinates.getMaxPageScaleFactor()
2756 - mRenderCoordinates.getPageScaleFactor();
2757 return zoomInExtent > ZOOM_CONTROLS_EPSILON;
2761 * Checks whether the ContentViewCore can be zoomed out.
2763 * @return True if the ContentViewCore can be zoomed out.
2765 // This method uses the term 'zoom' for legacy reasons, but relates
2766 // to what chrome calls the 'page scale factor'.
2767 public boolean canZoomOut() {
2768 final float zoomOutExtent = mRenderCoordinates.getPageScaleFactor()
2769 - mRenderCoordinates.getMinPageScaleFactor();
2770 return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
2774 * Zooms in the ContentViewCore by 25% (or less if that would result in
2775 * zooming in more than possible).
2777 * @return True if there was a zoom change, false otherwise.
2779 // This method uses the term 'zoom' for legacy reasons, but relates
2780 // to what chrome calls the 'page scale factor'.
2781 public boolean zoomIn() {
2785 return pinchByDelta(1.25f);
2789 * Zooms out the ContentViewCore by 20% (or less if that would result in
2790 * zooming out more than possible).
2792 * @return True if there was a zoom change, false otherwise.
2794 // This method uses the term 'zoom' for legacy reasons, but relates
2795 // to what chrome calls the 'page scale factor'.
2796 public boolean zoomOut() {
2797 if (!canZoomOut()) {
2800 return pinchByDelta(0.8f);
2804 * Resets the zoom factor of the ContentViewCore.
2806 * @return True if there was a zoom change, false otherwise.
2808 // This method uses the term 'zoom' for legacy reasons, but relates
2809 // to what chrome calls the 'page scale factor'.
2810 public boolean zoomReset() {
2811 // The page scale factor is initialized to mNativeMinimumScale when
2812 // the page finishes loading. Thus sets it back to mNativeMinimumScale.
2813 if (!canZoomOut()) return false;
2814 return pinchByDelta(
2815 mRenderCoordinates.getMinPageScaleFactor()
2816 / mRenderCoordinates.getPageScaleFactor());
2820 * Simulate a pinch zoom gesture.
2822 * @param delta the factor by which the current page scale should be multiplied by.
2823 * @return whether the gesture was sent.
2825 public boolean pinchByDelta(float delta) {
2826 if (mNativeContentViewCore == 0) return false;
2828 long timeMs = System.currentTimeMillis();
2829 int xPix = getViewportWidthPix() / 2;
2830 int yPix = getViewportHeightPix() / 2;
2832 nativePinchBegin(mNativeContentViewCore, timeMs, xPix, yPix);
2833 nativePinchBy(mNativeContentViewCore, timeMs, xPix, yPix, delta);
2834 nativePinchEnd(mNativeContentViewCore, timeMs);
2840 * Invokes the graphical zoom picker widget for this ContentView.
2842 public void invokeZoomPicker() {
2843 mZoomControlsDelegate.invokeZoomPicker();
2847 * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
2848 * and automatically pass in {@link JavascriptInterface} as the required annotation.
2850 * @param object The Java object to inject into the ContentViewCore's JavaScript context. Null
2851 * values are ignored.
2852 * @param name The name used to expose the instance in JavaScript.
2854 public void addJavascriptInterface(Object object, String name) {
2855 addPossiblyUnsafeJavascriptInterface(object, name, JavascriptInterface.class);
2859 * This method injects the supplied Java object into the ContentViewCore.
2860 * The object is injected into the JavaScript context of the main frame,
2861 * using the supplied name. This allows the Java object to be accessed from
2862 * JavaScript. Note that that injected objects will not appear in
2863 * JavaScript until the page is next (re)loaded. For example:
2864 * <pre> view.addJavascriptInterface(new Object(), "injectedObject");
2865 * view.loadData("<!DOCTYPE html><title></title>", "text/html", null);
2866 * view.loadUrl("javascript:alert(injectedObject.toString())");</pre>
2867 * <p><strong>IMPORTANT:</strong>
2869 * <li> addJavascriptInterface() can be used to allow JavaScript to control
2870 * the host application. This is a powerful feature, but also presents a
2871 * security risk. Use of this method in a ContentViewCore containing
2872 * untrusted content could allow an attacker to manipulate the host
2873 * application in unintended ways, executing Java code with the permissions
2874 * of the host application. Use extreme care when using this method in a
2875 * ContentViewCore which could contain untrusted content. Particular care
2876 * should be taken to avoid unintentional access to inherited methods, such
2877 * as {@link Object#getClass()}. To prevent access to inherited methods,
2878 * pass an annotation for {@code requiredAnnotation}. This will ensure
2879 * that only methods with {@code requiredAnnotation} are exposed to the
2880 * Javascript layer. {@code requiredAnnotation} will be passed to all
2881 * subsequently injected Java objects if any methods return an object. This
2882 * means the same restrictions (or lack thereof) will apply. Alternatively,
2883 * {@link #addJavascriptInterface(Object, String)} can be called, which
2884 * automatically uses the {@link JavascriptInterface} annotation.
2885 * <li> JavaScript interacts with Java objects on a private, background
2886 * thread of the ContentViewCore. Care is therefore required to maintain
2887 * thread safety.</li>
2890 * @param object The Java object to inject into the
2891 * ContentViewCore's JavaScript context. Null
2892 * values are ignored.
2893 * @param name The name used to expose the instance in
2895 * @param requiredAnnotation Restrict exposed methods to ones with this
2896 * annotation. If {@code null} all methods are
2900 public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
2901 Class<? extends Annotation> requiredAnnotation) {
2902 if (mNativeContentViewCore != 0 && object != null) {
2903 mJavaScriptInterfaces.put(name, object);
2904 nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation,
2905 mRetainedJavaScriptObjects);
2910 * Removes a previously added JavaScript interface with the given name.
2912 * @param name The name of the interface to remove.
2914 public void removeJavascriptInterface(String name) {
2915 mJavaScriptInterfaces.remove(name);
2916 if (mNativeContentViewCore != 0) {
2917 nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2922 * Return the current scale of the ContentView.
2923 * @return The current page scale factor.
2925 public float getScale() {
2926 return mRenderCoordinates.getPageScaleFactor();
2930 * If the view is ready to draw contents to the screen. In hardware mode,
2931 * the initialization of the surface texture may not occur until after the
2932 * view has been added to the layout. This method will return {@code true}
2933 * once the texture is actually ready.
2935 public boolean isReady() {
2936 if (mNativeContentViewCore == 0) return false;
2937 return nativeIsRenderWidgetHostViewReady(mNativeContentViewCore);
2941 private void startContentIntent(String contentUrl) {
2942 getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2946 public void onAccessibilityStateChanged(boolean enabled) {
2947 setAccessibilityState(enabled);
2951 * Determines whether or not this ContentViewCore can handle this accessibility action.
2952 * @param action The action to perform.
2953 * @return Whether or not this action is supported.
2955 public boolean supportsAccessibilityAction(int action) {
2956 return mAccessibilityInjector.supportsAccessibilityAction(action);
2960 * Attempts to perform an accessibility action on the web content. If the accessibility action
2961 * cannot be processed, it returns {@code null}, allowing the caller to know to call the
2962 * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value.
2963 * Otherwise the return value from this method should be used.
2964 * @param action The action to perform.
2965 * @param arguments Optional action arguments.
2966 * @return Whether the action was performed or {@code null} if the call should be delegated to
2967 * the super {@link View} class.
2969 public boolean performAccessibilityAction(int action, Bundle arguments) {
2970 if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2971 return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2978 * Set the BrowserAccessibilityManager, used for native accessibility
2979 * (not script injection). This is only set when system accessibility
2981 * @param manager The new BrowserAccessibilityManager.
2983 public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
2984 mBrowserAccessibilityManager = manager;
2988 * Get the BrowserAccessibilityManager, used for native accessibility
2989 * (not script injection). This will return null when system accessibility
2991 * @return This view's BrowserAccessibilityManager.
2993 public BrowserAccessibilityManager getBrowserAccessibilityManager() {
2994 return mBrowserAccessibilityManager;
2998 * If native accessibility (not script injection) is enabled, and if this is
2999 * running on JellyBean or later, returns an AccessibilityNodeProvider that
3000 * implements native accessibility for this view. Returns null otherwise.
3001 * Lazily initializes native accessibility here if it's allowed.
3002 * @return The AccessibilityNodeProvider, if available, or null otherwise.
3004 public AccessibilityNodeProvider getAccessibilityNodeProvider() {
3005 if (mBrowserAccessibilityManager != null) {
3006 return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
3009 if (mNativeAccessibilityAllowed &&
3010 !mNativeAccessibilityEnabled &&
3011 mNativeContentViewCore != 0 &&
3012 Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
3013 mNativeAccessibilityEnabled = true;
3014 nativeSetAccessibilityEnabled(mNativeContentViewCore, true);
3021 * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
3023 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
3024 // Note: this is only used by the script-injecting accessibility code.
3025 mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
3029 * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
3031 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
3032 // Note: this is only used by the script-injecting accessibility code.
3033 event.setClassName(this.getClass().getName());
3035 // Identify where the top-left of the screen currently points to.
3036 event.setScrollX(mRenderCoordinates.getScrollXPixInt());
3037 event.setScrollY(mRenderCoordinates.getScrollYPixInt());
3039 // The maximum scroll values are determined by taking the content dimensions and
3040 // subtracting off the actual dimensions of the ChromeView.
3041 int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt());
3042 int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt());
3043 event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0);
3045 // Setting the maximum scroll values requires API level 15 or higher.
3046 final int SDK_VERSION_REQUIRED_TO_SET_SCROLL = 15;
3047 if (Build.VERSION.SDK_INT >= SDK_VERSION_REQUIRED_TO_SET_SCROLL) {
3048 event.setMaxScrollX(maxScrollXPix);
3049 event.setMaxScrollY(maxScrollYPix);
3054 * Returns whether accessibility script injection is enabled on the device
3056 public boolean isDeviceAccessibilityScriptInjectionEnabled() {
3058 if (CommandLine.getInstance().hasSwitch(
3059 ContentSwitches.DISABLE_ACCESSIBILITY_SCRIPT_INJECTION)) {
3063 if (!mContentSettings.getJavaScriptEnabled()) {
3067 int result = getContext().checkCallingOrSelfPermission(
3068 android.Manifest.permission.INTERNET);
3069 if (result != PackageManager.PERMISSION_GRANTED) {
3073 Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
3074 field.setAccessible(true);
3075 String accessibilityScriptInjection = (String) field.get(null);
3076 ContentResolver contentResolver = getContext().getContentResolver();
3078 if (mAccessibilityScriptInjectionObserver == null) {
3079 ContentObserver contentObserver = new ContentObserver(new Handler()) {
3081 public void onChange(boolean selfChange, Uri uri) {
3082 setAccessibilityState(mAccessibilityManager.isEnabled());
3085 contentResolver.registerContentObserver(
3086 Settings.Secure.getUriFor(accessibilityScriptInjection),
3089 mAccessibilityScriptInjectionObserver = contentObserver;
3092 return Settings.Secure.getInt(contentResolver, accessibilityScriptInjection, 0) == 1;
3093 } catch (NoSuchFieldException e) {
3094 // Do nothing, default to false.
3095 } catch (IllegalAccessException e) {
3096 // Do nothing, default to false.
3102 * Returns whether or not accessibility injection is being used.
3104 public boolean isInjectingAccessibilityScript() {
3105 return mAccessibilityInjector.accessibilityIsAvailable();
3109 * Turns browser accessibility on or off.
3110 * If |state| is |false|, this turns off both native and injected accessibility.
3111 * Otherwise, if accessibility script injection is enabled, this will enable the injected
3112 * accessibility scripts. Native accessibility is enabled on demand.
3114 public void setAccessibilityState(boolean state) {
3116 setInjectedAccessibility(false);
3117 mNativeAccessibilityAllowed = false;
3119 boolean useScriptInjection = isDeviceAccessibilityScriptInjectionEnabled();
3120 setInjectedAccessibility(useScriptInjection);
3121 mNativeAccessibilityAllowed = !useScriptInjection;
3126 * Enable or disable injected accessibility features
3128 public void setInjectedAccessibility(boolean enabled) {
3129 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
3130 mAccessibilityInjector.setScriptEnabled(enabled);
3134 * Stop any TTS notifications that are currently going on.
3136 public void stopCurrentAccessibilityNotifications() {
3137 mAccessibilityInjector.onPageLostFocus();
3141 * Inform WebKit that Fullscreen mode has been exited by the user.
3143 public void exitFullscreen() {
3144 if (mNativeContentViewCore != 0) nativeExitFullscreen(mNativeContentViewCore);
3148 * Changes whether hiding the top controls is enabled.
3150 * @param enableHiding Whether hiding the top controls should be enabled or not.
3151 * @param enableShowing Whether showing the top controls should be enabled or not.
3152 * @param animate Whether the transition should be animated or not.
3154 public void updateTopControlsState(boolean enableHiding, boolean enableShowing,
3156 if (mNativeContentViewCore != 0) {
3157 nativeUpdateTopControlsState(
3158 mNativeContentViewCore, enableHiding, enableShowing, animate);
3163 * Callback factory method for nativeGetNavigationHistory().
3166 private void addToNavigationHistory(Object history, int index, String url, String virtualUrl,
3167 String originalUrl, String title, Bitmap favicon) {
3168 NavigationEntry entry = new NavigationEntry(
3169 index, url, virtualUrl, originalUrl, title, favicon);
3170 ((NavigationHistory) history).addEntry(entry);
3174 * Get a copy of the navigation history of the view.
3176 public NavigationHistory getNavigationHistory() {
3177 NavigationHistory history = new NavigationHistory();
3178 if (mNativeContentViewCore != 0) {
3179 int currentIndex = nativeGetNavigationHistory(mNativeContentViewCore, history);
3180 history.setCurrentEntryIndex(currentIndex);
3186 public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
3187 NavigationHistory history = new NavigationHistory();
3188 if (mNativeContentViewCore != 0) {
3189 nativeGetDirectedNavigationHistory(
3190 mNativeContentViewCore, history, isForward, itemLimit);
3196 * @return The original request URL for the current navigation entry, or null if there is no
3199 public String getOriginalUrlForActiveNavigationEntry() {
3200 if (mNativeContentViewCore != 0) {
3201 return nativeGetOriginalUrlForActiveNavigationEntry(mNativeContentViewCore);
3207 * @return The cached copy of render positions and scales.
3209 public RenderCoordinates getRenderCoordinates() {
3210 return mRenderCoordinates;
3214 private int getLocationInWindowX() {
3215 return mLocationInWindowX;
3219 private int getLocationInWindowY() {
3220 return mLocationInWindowY;
3224 private static Rect createRect(int x, int y, int right, int bottom) {
3225 return new Rect(x, y, right, bottom);
3228 public void attachExternalVideoSurface(int playerId, Surface surface) {
3229 if (mNativeContentViewCore != 0) {
3230 nativeAttachExternalVideoSurface(mNativeContentViewCore, playerId, surface);
3234 public void detachExternalVideoSurface(int playerId) {
3235 if (mNativeContentViewCore != 0) {
3236 nativeDetachExternalVideoSurface(mNativeContentViewCore, playerId);
3240 private boolean onAnimate(long frameTimeMicros) {
3241 if (mNativeContentViewCore == 0) return false;
3242 return nativeOnAnimate(mNativeContentViewCore, frameTimeMicros);
3245 private void animateIfNecessary(long frameTimeMicros) {
3247 mNeedAnimate = onAnimate(frameTimeMicros);
3248 if (!mNeedAnimate) removeVSyncSubscriber();
3253 private void notifyExternalSurface(
3254 int playerId, boolean isRequest, float x, float y, float width, float height) {
3255 if (isRequest) getContentViewClient().onExternalVideoSurfaceRequested(playerId);
3256 getContentViewClient().onGeometryChanged(playerId, new RectF(x, y, x + width, y + height));
3259 public void extractSmartClipData(int x, int y, int width, int height) {
3260 if (mNativeContentViewCore != 0) {
3261 nativeExtractSmartClipData(mNativeContentViewCore, x, y, width, height);
3266 private void onSmartClipDataExtracted(String result) {
3267 if (mSmartClipDataListener != null ) {
3268 mSmartClipDataListener.onSmartClipDataExtracted(result);
3272 public void setSmartClipDataListener(SmartClipDataListener listener) {
3273 mSmartClipDataListener = listener;
3277 * Offer a long press gesture to the embedding View, primarily for WebView compatibility.
3279 * @return true if the embedder handled the event.
3281 private boolean offerLongPressToEmbedder() {
3282 return mContainerView.performLongClick();
3285 private native long nativeInit(long webContentsPtr,
3286 long viewAndroidPtr, long windowAndroidPtr);
3289 private ContentVideoViewClient getContentVideoViewClient() {
3290 return getContentViewClient().getContentVideoViewClient();
3294 private boolean shouldBlockMediaRequest(String url) {
3295 return getContentViewClient().shouldBlockMediaRequest(url);
3299 private void onNativeFlingStopped() {
3300 updateGestureStateListener(GestureEventType.FLING_END);
3303 private native WebContents nativeGetWebContentsAndroid(long nativeContentViewCoreImpl);
3305 private native void nativeOnJavaContentViewCoreDestroyed(long nativeContentViewCoreImpl);
3307 private native void nativeLoadUrl(
3308 long nativeContentViewCoreImpl,
3312 int uaOverrideOption,
3313 String extraHeaders,
3315 String baseUrlForDataUrl,
3316 String virtualUrlForDataUrl,
3317 boolean canLoadLocalResources);
3319 private native String nativeGetURL(long nativeContentViewCoreImpl);
3321 private native String nativeGetTitle(long nativeContentViewCoreImpl);
3323 private native void nativeShowInterstitialPage(
3324 long nativeContentViewCoreImpl, String url, long nativeInterstitialPageDelegateAndroid);
3325 private native boolean nativeIsShowingInterstitialPage(long nativeContentViewCoreImpl);
3327 private native boolean nativeIsIncognito(long nativeContentViewCoreImpl);
3329 private native void nativeSetFocus(long nativeContentViewCoreImpl, boolean focused);
3331 private native void nativeSendOrientationChangeEvent(
3332 long nativeContentViewCoreImpl, int orientation);
3334 // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
3335 private native void nativeOnTouchEventHandlingBegin(
3336 long nativeContentViewCoreImpl, MotionEvent event);
3338 private native void nativeOnTouchEventHandlingEnd(long nativeContentViewCoreImpl);
3340 private native int nativeSendMouseMoveEvent(
3341 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3343 private native int nativeSendMouseWheelEvent(
3344 long nativeContentViewCoreImpl, long timeMs, float x, float y, float verticalAxis);
3346 private native void nativeScrollBegin(
3347 long nativeContentViewCoreImpl, long timeMs, float x, float y, float hintX,
3350 private native void nativeScrollEnd(long nativeContentViewCoreImpl, long timeMs);
3352 private native void nativeScrollBy(
3353 long nativeContentViewCoreImpl, long timeMs, float x, float y,
3354 float deltaX, float deltaY);
3356 private native void nativeFlingStart(
3357 long nativeContentViewCoreImpl, long timeMs, float x, float y, float vx, float vy);
3359 private native void nativeFlingCancel(long nativeContentViewCoreImpl, long timeMs);
3361 private native void nativeSingleTap(
3362 long nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
3364 private native void nativeSingleTapUnconfirmed(
3365 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3367 private native void nativeShowPress(
3368 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3370 private native void nativeTapCancel(
3371 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3373 private native void nativeTapDown(
3374 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3376 private native void nativeDoubleTap(
3377 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3379 private native void nativeLongPress(
3380 long nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
3382 private native void nativeLongTap(
3383 long nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
3385 private native void nativePinchBegin(
3386 long nativeContentViewCoreImpl, long timeMs, float x, float y);
3388 private native void nativePinchEnd(long nativeContentViewCoreImpl, long timeMs);
3390 private native void nativePinchBy(long nativeContentViewCoreImpl, long timeMs,
3391 float anchorX, float anchorY, float deltaScale);
3393 private native void nativeSelectBetweenCoordinates(
3394 long nativeContentViewCoreImpl, float x1, float y1, float x2, float y2);
3396 private native void nativeMoveCaret(long nativeContentViewCoreImpl, float x, float y);
3398 private native void nativeLoadIfNecessary(long nativeContentViewCoreImpl);
3399 private native void nativeRequestRestoreLoad(long nativeContentViewCoreImpl);
3401 private native void nativeStopLoading(long nativeContentViewCoreImpl);
3403 private native void nativeReload(long nativeContentViewCoreImpl, boolean checkForRepost);
3404 private native void nativeReloadIgnoringCache(
3405 long nativeContentViewCoreImpl, boolean checkForRepost);
3407 private native void nativeCancelPendingReload(long nativeContentViewCoreImpl);
3409 private native void nativeContinuePendingReload(long nativeContentViewCoreImpl);
3411 private native void nativeSelectPopupMenuItems(long nativeContentViewCoreImpl, int[] indices);
3413 private native void nativeScrollFocusedEditableNodeIntoView(long nativeContentViewCoreImpl);
3414 private native void nativeUndoScrollFocusedEditableNodeIntoView(long nativeContentViewCoreImpl);
3416 private native void nativeClearHistory(long nativeContentViewCoreImpl);
3418 private native void nativeEvaluateJavaScript(long nativeContentViewCoreImpl,
3419 String script, JavaScriptCallback callback, boolean startRenderer);
3421 private native int nativeGetNativeImeAdapter(long nativeContentViewCoreImpl);
3423 private native int nativeGetCurrentRenderProcessId(long nativeContentViewCoreImpl);
3425 private native int nativeGetBackgroundColor(long nativeContentViewCoreImpl);
3427 private native void nativeOnShow(long nativeContentViewCoreImpl);
3428 private native void nativeOnHide(long nativeContentViewCoreImpl);
3430 private native void nativeSetUseDesktopUserAgent(long nativeContentViewCoreImpl,
3431 boolean enabled, boolean reloadOnChange);
3432 private native boolean nativeGetUseDesktopUserAgent(long nativeContentViewCoreImpl);
3434 private native void nativeClearSslPreferences(long nativeContentViewCoreImpl);
3436 private native void nativeAddJavascriptInterface(long nativeContentViewCoreImpl, Object object,
3437 String name, Class requiredAnnotation, HashSet<Object> retainedObjectSet);
3439 private native void nativeRemoveJavascriptInterface(long nativeContentViewCoreImpl,
3442 private native int nativeGetNavigationHistory(long nativeContentViewCoreImpl, Object context);
3443 private native void nativeGetDirectedNavigationHistory(long nativeContentViewCoreImpl,
3444 Object context, boolean isForward, int maxEntries);
3445 private native String nativeGetOriginalUrlForActiveNavigationEntry(
3446 long nativeContentViewCoreImpl);
3448 private native void nativeUpdateVSyncParameters(long nativeContentViewCoreImpl,
3449 long timebaseMicros, long intervalMicros);
3451 private native void nativeOnVSync(long nativeContentViewCoreImpl, long frameTimeMicros);
3453 private native boolean nativeOnAnimate(long nativeContentViewCoreImpl, long frameTimeMicros);
3455 private native boolean nativePopulateBitmapFromCompositor(long nativeContentViewCoreImpl,
3458 private native void nativeWasResized(long nativeContentViewCoreImpl);
3460 private native boolean nativeIsRenderWidgetHostViewReady(long nativeContentViewCoreImpl);
3462 private native void nativeExitFullscreen(long nativeContentViewCoreImpl);
3463 private native void nativeUpdateTopControlsState(long nativeContentViewCoreImpl,
3464 boolean enableHiding, boolean enableShowing, boolean animate);
3466 private native void nativeShowImeIfNeeded(long nativeContentViewCoreImpl);
3468 private native void nativeAttachExternalVideoSurface(
3469 long nativeContentViewCoreImpl, int playerId, Surface surface);
3471 private native void nativeDetachExternalVideoSurface(
3472 long nativeContentViewCoreImpl, int playerId);
3474 private native void nativeSetAccessibilityEnabled(
3475 long nativeContentViewCoreImpl, boolean enabled);
3477 private native void nativeSendSingleTapUma(long nativeContentViewCoreImpl,
3478 int type, int count);
3480 private native void nativeSendActionAfterDoubleTapUma(long nativeContentViewCoreImpl,
3481 int type, boolean hasDelay, int count);
3483 private native void nativeExtractSmartClipData(long nativeContentViewCoreImpl,
3484 int x, int y, int w, int h);