Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / browser / ContentViewCore.java
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.
4
5 package org.chromium.content.browser;
6
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;
51
52 import com.google.common.annotations.VisibleForTesting;
53
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;
81
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;
88 import java.util.Map;
89
90 /**
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.
94  */
95 @JNINamespace("content")
96 public class ContentViewCore
97         implements MotionEventDelegate, NavigationClient, AccessibilityStateChangeListener {
98
99     private static final String TAG = "ContentViewCore";
100
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;
104
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;
108
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;
111
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>();
123
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>();
131
132     /**
133      * Interface that consumers of {@link ContentViewCore} must implement to allow the proper
134      * dispatching of view methods through the containing view.
135      *
136      * <p>
137      * All methods with the "super_" prefix should be routed to the parent of the
138      * implementing container view.
139      */
140     @SuppressWarnings("javadoc")
141     public interface InternalAccessDelegate {
142         /**
143          * @see View#drawChild(Canvas, View, long)
144          */
145         boolean drawChild(Canvas canvas, View child, long drawingTime);
146
147         /**
148          * @see View#onKeyUp(keyCode, KeyEvent)
149          */
150         boolean super_onKeyUp(int keyCode, KeyEvent event);
151
152         /**
153          * @see View#dispatchKeyEventPreIme(KeyEvent)
154          */
155         boolean super_dispatchKeyEventPreIme(KeyEvent event);
156
157         /**
158          * @see View#dispatchKeyEvent(KeyEvent)
159          */
160         boolean super_dispatchKeyEvent(KeyEvent event);
161
162         /**
163          * @see View#onGenericMotionEvent(MotionEvent)
164          */
165         boolean super_onGenericMotionEvent(MotionEvent event);
166
167         /**
168          * @see View#onConfigurationChanged(Configuration)
169          */
170         void super_onConfigurationChanged(Configuration newConfig);
171
172         /**
173          * @see View#onScrollChanged(int, int, int, int)
174          */
175         void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix);
176
177         /**
178          * @see View#awakenScrollBars()
179          */
180         boolean awakenScrollBars();
181
182         /**
183          * @see View#awakenScrollBars(int, boolean)
184          */
185         boolean super_awakenScrollBars(int startDelay, boolean invalidate);
186     }
187
188     /**
189      * An interface for controlling visibility and state of embedder-provided zoom controls.
190      */
191     public interface ZoomControlsDelegate {
192         /**
193          * Called when it's reasonable to show zoom controls.
194          */
195         void invokeZoomPicker();
196
197         /**
198          * Called when zoom controls need to be hidden (e.g. when the view hides).
199          */
200         void dismissZoomPicker();
201
202         /**
203          * Called when page scale has been changed, so the controls can update their state.
204          */
205         void updateZoomControls();
206     }
207
208     /**
209      * An interface that allows the embedder to be notified when the results of
210      * extractSmartClipData are available.
211      */
212     public interface SmartClipDataListener {
213         public void onSmartClipDataExtracted(String result);
214     }
215
216     private VSyncManager.Provider mVSyncProvider;
217     private VSyncManager.Listener mVSyncListener;
218     private int mVSyncSubscriberCount;
219     private boolean mVSyncListenerRegistered;
220
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;
225
226     public VSyncManager.Listener getVSyncListener(VSyncManager.Provider vsyncProvider) {
227         if (mVSyncProvider != null && mVSyncListenerRegistered) {
228             mVSyncProvider.unregisterVSyncListener(mVSyncListener);
229             mVSyncListenerRegistered = false;
230         }
231
232         mVSyncProvider = vsyncProvider;
233         mVSyncListener = new VSyncManager.Listener() {
234             @Override
235             public void updateVSync(long tickTimeMicros, long intervalMicros) {
236                 if (mNativeContentViewCore != 0) {
237                     nativeUpdateVSyncParameters(mNativeContentViewCore, tickTimeMicros,
238                             intervalMicros);
239                 }
240             }
241
242             @Override
243             public void onVSync(long frameTimeMicros) {
244                 animateIfNecessary(frameTimeMicros);
245
246                 if (mRequestedVSyncForInput) {
247                     mRequestedVSyncForInput = false;
248                     removeVSyncSubscriber();
249                 }
250                 if (mNativeContentViewCore != 0) {
251                     nativeOnVSync(mNativeContentViewCore, frameTimeMicros);
252                 }
253             }
254         };
255
256         if (mVSyncSubscriberCount > 0) {
257             // addVSyncSubscriber() is called before getVSyncListener.
258             vsyncProvider.registerVSyncListener(mVSyncListener);
259             mVSyncListenerRegistered = true;
260         }
261
262         return mVSyncListener;
263     }
264
265     @CalledByNative
266     void addVSyncSubscriber() {
267         if (!isVSyncNotificationEnabled()) {
268             mDidSignalVSyncUsingInputEvent = false;
269         }
270         if (mVSyncProvider != null && !mVSyncListenerRegistered) {
271             mVSyncProvider.registerVSyncListener(mVSyncListener);
272             mVSyncListenerRegistered = true;
273         }
274         mVSyncSubscriberCount++;
275     }
276
277     @CalledByNative
278     void removeVSyncSubscriber() {
279         if (mVSyncProvider != null && mVSyncSubscriberCount == 1) {
280             assert mVSyncListenerRegistered;
281             mVSyncProvider.unregisterVSyncListener(mVSyncListener);
282             mVSyncListenerRegistered = false;
283         }
284         mVSyncSubscriberCount--;
285         assert mVSyncSubscriberCount >= 0;
286     }
287
288     @CalledByNative
289     private void resetVSyncNotification() {
290         while (isVSyncNotificationEnabled()) removeVSyncSubscriber();
291         mVSyncSubscriberCount = 0;
292         mVSyncListenerRegistered = false;
293         mNeedAnimate = false;
294     }
295
296     private boolean isVSyncNotificationEnabled() {
297         return mVSyncProvider != null && mVSyncListenerRegistered;
298     }
299
300     @CalledByNative
301     private void setNeedsAnimate() {
302         if (!mNeedAnimate) {
303             mNeedAnimate = true;
304             addVSyncSubscriber();
305         }
306     }
307
308     private final Context mContext;
309     private ViewGroup mContainerView;
310     private InternalAccessDelegate mContainerViewInternals;
311     private WebContents mWebContents;
312     private WebContentsObserverAndroid mWebContentsObserver;
313
314     private ContentViewClient mContentViewClient;
315
316     private ContentSettings mContentSettings;
317
318     // Native pointer to C++ ContentViewCoreImpl object which will be set by nativeInit().
319     private long mNativeContentViewCore = 0;
320
321     private boolean mInForeground = false;
322
323     private ContentViewGestureHandler mContentViewGestureHandler;
324     private final ObserverList<GestureStateListener> mGestureStateListeners;
325     private final RewindableIterator<GestureStateListener> mGestureStateListenersIterator;
326     private ZoomControlsDelegate mZoomControlsDelegate;
327
328     private PopupZoomer mPopupZoomer;
329
330     private Runnable mFakeMouseMoveRunnable = null;
331
332     // Only valid when focused on a text / password field.
333     private ImeAdapter mImeAdapter;
334     private ImeAdapter.AdapterInputConnectionFactory mAdapterInputConnectionFactory;
335     private AdapterInputConnection mInputConnection;
336
337     private SelectionHandleController mSelectionHandleController;
338     private InsertionHandleController mInsertionHandleController;
339
340     private Runnable mDeferredHandleFadeInRunnable;
341
342     private PositionObserver mPositionObserver;
343     private PositionObserver.Listener mPositionListener;
344
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;
355
356     // Cached copy of all positions and scales as reported by the renderer.
357     private final RenderCoordinates mRenderCoordinates;
358
359     private final RenderCoordinates.NormalizedPoint mStartHandlePoint;
360     private final RenderCoordinates.NormalizedPoint mEndHandlePoint;
361     private final RenderCoordinates.NormalizedPoint mInsertionHandlePoint;
362
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;
370
371     // Delegate that will handle GET downloads, and be notified of completion of POST downloads.
372     private ContentViewDownloadDelegate mDownloadDelegate;
373
374     // The AccessibilityInjector that handles loading Accessibility scripts into the web page.
375     private AccessibilityInjector mAccessibilityInjector;
376
377     // Whether native accessibility, i.e. without any script injection, is allowed.
378     private boolean mNativeAccessibilityAllowed;
379
380     // Whether native accessibility, i.e. without any script injection, has been enabled.
381     private boolean mNativeAccessibilityEnabled;
382
383     // Handles native accessibility, i.e. without any script injection.
384     private BrowserAccessibilityManager mBrowserAccessibilityManager;
385
386     // System accessibility service.
387     private final AccessibilityManager mAccessibilityManager;
388
389     // Allows us to dynamically respond when the accessibility script injection flag changes.
390     private ContentObserver mAccessibilityScriptInjectionObserver;
391
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();
396
397     // Used to keep track of whether we should try to undo the last zoom-to-textfield operation.
398     private boolean mScrolledAndZoomedFocusedEditableNode = false;
399
400     // Whether we received a new frame since consumePendingRendererFrame() was last called.
401     private boolean mPendingRendererFrame = false;
402
403     // Whether we should animate at the next vsync tick.
404     private boolean mNeedAnimate = false;
405
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
410     // vsync.
411     private boolean mRequestedVSyncForInput = false;
412
413     // Used for tracking UMA ActionAfterDoubleTap to tell user's immediate
414     // action after a double tap.
415     private long mLastDoubleTapTimeMs;
416
417     // On single tap this will store the x, y coordinates of the touch.
418     private int mSingleTapX;
419     private int mSingleTapY;
420
421     private ViewAndroid mViewAndroid;
422
423     private SmartClipDataListener mSmartClipDataListener = null;
424
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;
431     }
432
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;
438     }
439
440     /**
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.
443      */
444     private static final long ACTION_AFTER_DOUBLE_TAP_WINDOW_MS = 5000;
445
446     /**
447      * Constructs a new ContentViewCore. Embedders must call initialize() after constructing
448      * a ContentViewCore and before using it.
449      *
450      * @param context The context used to create this.
451      */
452     public ContentViewCore(Context context) {
453         mContext = context;
454
455         WeakContext.initializeWeakContext(context);
456         HeapStatsLogger.init(mContext.getApplicationContext());
457         mAdapterInputConnectionFactory = new AdapterInputConnectionFactory();
458
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();
469     }
470
471     /**
472      * @return The context used for creating this ContentViewCore.
473      */
474     @CalledByNative
475     public Context getContext() {
476         return mContext;
477     }
478
479     /**
480      * @return The ViewGroup that all view actions of this ContentViewCore should interact with.
481      */
482     public ViewGroup getContainerView() {
483         return mContainerView;
484     }
485
486     /**
487      * @return The WebContents currently being rendered.
488      */
489     public WebContents getWebContents() {
490         return mWebContents;
491     }
492
493     /**
494      * Specifies how much smaller the WebKit layout size should be relative to the size of this
495      * view.
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.
498      */
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);
505         }
506     }
507
508     /**
509      * Returns a delegate that can be used to add and remove views from the ContainerView.
510      *
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
516      *
517      * @return A ViewAndroidDelegate that can be used to add and remove views.
518      */
519     @VisibleForTesting
520     public ViewAndroidDelegate getViewAndroidDelegate() {
521         return new ViewAndroidDelegate() {
522             @Override
523             public View acquireAnchorView() {
524                 View anchorView = new View(getContext());
525                 mContainerView.addView(anchorView);
526                 return anchorView;
527             }
528
529             @Override
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;
534
535                 float scale = (float) DeviceDisplayInfo.create(getContext()).getDIPScale();
536
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;
545                     }
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
556                     // these models.
557                     leftMargin += mRenderCoordinates.getScrollXPixInt();
558                     topMargin += mRenderCoordinates.getScrollYPixInt();
559
560                     android.widget.AbsoluteLayout.LayoutParams lp =
561                             new android.widget.AbsoluteLayout.LayoutParams(
562                                 scaledWidth, (int) (height * scale), leftMargin, topMargin);
563                     view.setLayoutParams(lp);
564                 } else {
565                     Log.e(TAG, "Unknown layout " + mContainerView.getClass().getName());
566                 }
567             }
568
569             @Override
570             public void releaseAnchorView(View anchorView) {
571                 mContainerView.removeView(anchorView);
572             }
573         };
574     }
575
576     @VisibleForTesting
577     public void setImeAdapterForTest(ImeAdapter imeAdapter) {
578         mImeAdapter = imeAdapter;
579     }
580
581     @VisibleForTesting
582     public ImeAdapter getImeAdapterForTest() {
583         return mImeAdapter;
584     }
585
586     @VisibleForTesting
587     public void setAdapterInputConnectionFactory(AdapterInputConnectionFactory factory) {
588         mAdapterInputConnectionFactory = factory;
589     }
590
591     @VisibleForTesting
592     public AdapterInputConnection getInputConnectionForTest() {
593         return mInputConnection;
594     }
595
596     private ImeAdapter createImeAdapter(Context context) {
597         return new ImeAdapter(new InputMethodManagerWrapper(context),
598                 new ImeAdapter.ImeAdapterDelegate() {
599                     @Override
600                     public void onImeEvent(boolean isFinish) {
601                         getContentViewClient().onImeEvent();
602                         if (!isFinish) {
603                             hideHandles();
604                             undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
605                         }
606                     }
607
608                     @Override
609                     public void onSetFieldValue() {
610                         scrollFocusedEditableNodeIntoView();
611                     }
612
613                     @Override
614                     public void onDismissInput() {
615                         getContentViewClient().onImeStateChangeRequested(false);
616                     }
617
618                     @Override
619                     public View getAttachedView() {
620                         return mContainerView;
621                     }
622
623                     @Override
624                     public ResultReceiver getNewShowKeyboardReceiver() {
625                         return new ResultReceiver(new Handler()) {
626                             @Override
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
634                                     // new size).
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();
643                                 } else {
644                                     undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
645                                 }
646                             }
647                         };
648                     }
649                 }
650         );
651     }
652
653     /**
654      *
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
657      *                           containerView.
658      * @param nativeWebContents A pointer to the native web contents.
659      * @param windowAndroid An instance of the WindowAndroid.
660      */
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() {
675             @Override
676             public void onPositionChanged(int x, int y) {
677                 if (isSelectionHandleShowing() || isInsertionHandleShowing()) {
678                     temporarilyHideTextHandles();
679                 }
680             }
681         };
682
683         long windowNativePointer = windowAndroid != null ? windowAndroid.getNativePointer() : 0;
684
685         long viewAndroidNativePointer = 0;
686         if (windowNativePointer != 0) {
687             mViewAndroid = new ViewAndroid(windowAndroid, getViewAndroidDelegate());
688             viewAndroidNativePointer = mViewAndroid.getNativePointer();
689         }
690
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() {
695             @Override
696             public void invokeZoomPicker() {}
697             @Override
698             public void dismissZoomPicker() {}
699             @Override
700             public void updateZoomControls() {}
701         };
702
703         mNativeContentViewCore = nativeInit(
704                 nativeWebContents, viewAndroidNativePointer, windowNativePointer);
705         mWebContents = nativeGetWebContentsAndroid(mNativeContentViewCore);
706         mContentSettings = new ContentSettings(this, mNativeContentViewCore);
707         initializeContainerView(internalDispatcher);
708
709         mAccessibilityInjector = AccessibilityInjector.newInstance(this);
710
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.");
714         } else {
715             contentDescription = mContext.getResources().getString(
716                     R.string.accessibility_content_view);
717         }
718         mContainerView.setContentDescription(contentDescription);
719         mWebContentsObserver = new WebContentsObserverAndroid(this) {
720             @Override
721             public void didStartLoading(String url) {
722                 hidePopupDialog();
723                 resetGestureDetectors();
724             }
725         };
726
727         sendOrientationChangeEvent();
728     }
729
730     @CalledByNative
731     void onNativeContentViewCoreDestroyed(long nativeContentViewCore) {
732         assert nativeContentViewCore == mNativeContentViewCore;
733         mNativeContentViewCore = 0;
734     }
735
736     /**
737      * Set the Container view Internals.
738      * @param internalDispatcher Handles dispatching all hidden or super methods to the
739      *                           containerView.
740      */
741     public void setContainerViewInternals(InternalAccessDelegate internalDispatcher) {
742         mContainerViewInternals = internalDispatcher;
743     }
744
745     /**
746      * Initializes the View that will contain all Views created by the ContentViewCore.
747      *
748      * @param internalDispatcher Handles dispatching all hidden or super methods to the
749      *                           containerView.
750      */
751     private void initializeContainerView(InternalAccessDelegate internalDispatcher) {
752         TraceEvent.begin();
753         mContainerViewInternals = internalDispatcher;
754
755         mContainerView.setWillNotDraw(false);
756         mContainerView.setClickable(true);
757
758         mRenderCoordinates.reset();
759         onRenderCoordinatesUpdated();
760
761         initPopupZoomer(mContext);
762         mImeAdapter = createImeAdapter(mContext);
763         TraceEvent.end();
764     }
765
766     private void initPopupZoomer(Context context) {
767         mPopupZoomer = new PopupZoomer(context);
768         mPopupZoomer.setOnVisibilityChangedListener(new PopupZoomer.OnVisibilityChangedListener() {
769             @Override
770             public void onPopupZoomerShown(final PopupZoomer zoomer) {
771                 mContainerView.post(new Runnable() {
772                     @Override
773                     public void run() {
774                         if (mContainerView.indexOfChild(zoomer) == -1) {
775                             mContainerView.addView(zoomer);
776                         } else {
777                             assert false : "PopupZoomer should never be shown without being hidden";
778                         }
779                     }
780                 });
781             }
782
783             @Override
784             public void onPopupZoomerHidden(final PopupZoomer zoomer) {
785                 mContainerView.post(new Runnable() {
786                     @Override
787                     public void run() {
788                         if (mContainerView.indexOfChild(zoomer) != -1) {
789                             mContainerView.removeView(zoomer);
790                             mContainerView.invalidate();
791                         } else {
792                             assert false : "PopupZoomer should never be hidden without being shown";
793                         }
794                     }
795                 });
796             }
797         });
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() {
801             @Override
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);
807                 }
808                 return true;
809             }
810
811             @Override
812             public boolean onLongPress(View v, MotionEvent e) {
813                 if (mNativeContentViewCore != 0) {
814                     nativeLongPress(mNativeContentViewCore, e.getEventTime(),
815                             e.getX(), e.getY(), true);
816                 }
817                 return true;
818             }
819         };
820         mPopupZoomer.setOnTapListener(listener);
821     }
822
823     /**
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
827      * been called.
828      */
829     public void destroy() {
830         if (mNativeContentViewCore != 0) {
831             nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore);
832         }
833         mWebContents = null;
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();
843     }
844
845     private void unregisterAccessibilityContentObserver() {
846         if (mAccessibilityScriptInjectionObserver == null) {
847             return;
848         }
849         getContext().getContentResolver().unregisterContentObserver(
850                 mAccessibilityScriptInjectionObserver);
851         mAccessibilityScriptInjectionObserver = null;
852     }
853
854     /**
855      * Returns true initially, false after destroy() has been called.
856      * It is illegal to call any other public method after destroy().
857      */
858     public boolean isAlive() {
859         return mNativeContentViewCore != 0;
860     }
861
862     /**
863      * This is only useful for passing over JNI to native code that requires ContentViewCore*.
864      * @return native ContentViewCore pointer.
865      */
866     @CalledByNative
867     public long getNativeContentViewCore() {
868         return mNativeContentViewCore;
869     }
870
871     public void setContentViewClient(ContentViewClient client) {
872         if (client == null) {
873             throw new IllegalArgumentException("The client can't be null.");
874         }
875         mContentViewClient = client;
876     }
877
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.
887         }
888         return mContentViewClient;
889     }
890
891     public int getBackgroundColor() {
892         if (mNativeContentViewCore != 0) {
893             return nativeGetBackgroundColor(mNativeContentViewCore);
894         }
895         return Color.WHITE;
896     }
897
898     @CalledByNative
899     private void onBackgroundColorChanged(int color) {
900         getContentViewClient().onBackgroundColorChanged(color);
901     }
902
903     /**
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).
907      *
908      * @param params Parameters for this load.
909      */
910     public void loadUrl(LoadUrlParams params) {
911         if (mNativeContentViewCore == 0) return;
912
913         nativeLoadUrl(mNativeContentViewCore,
914                 params.mUrl,
915                 params.mLoadUrlType,
916                 params.mTransitionType,
917                 params.mUaOverrideOption,
918                 params.getExtraHeadersString(),
919                 params.mPostData,
920                 params.mBaseUrlForDataUrl,
921                 params.mVirtualUrlForDataUrl,
922                 params.mCanLoadLocalResources);
923     }
924
925     /**
926      * Stops loading the current web contents.
927      */
928     public void stopLoading() {
929         reportActionAfterDoubleTapUMA(UMAActionAfterDoubleTap.NAVIGATE_STOP);
930         if (mNativeContentViewCore != 0) nativeStopLoading(mNativeContentViewCore);
931     }
932
933     /**
934      * Get the URL of the current page.
935      *
936      * @return The URL of the current page.
937      */
938     public String getUrl() {
939         if (mNativeContentViewCore != 0) return nativeGetURL(mNativeContentViewCore);
940         return null;
941     }
942
943     /**
944      * Get the title of the current page.
945      *
946      * @return The title of the current page.
947      */
948     public String getTitle() {
949         if (mNativeContentViewCore != 0) return nativeGetTitle(mNativeContentViewCore);
950         return null;
951     }
952
953     /**
954      * Shows an interstitial page driven by the passed in delegate.
955      *
956      * @param url The URL being blocked by the interstitial.
957      * @param delegate The delegate handling the interstitial.
958      */
959     @VisibleForTesting
960     public void showInterstitialPage(
961             String url, InterstitialPageDelegateAndroid delegate) {
962         if (mNativeContentViewCore == 0) return;
963         nativeShowInterstitialPage(mNativeContentViewCore, url, delegate.getNative());
964     }
965
966     /**
967      * @return Whether the page is currently showing an interstitial, such as a bad HTTPS page.
968      */
969     public boolean isShowingInterstitialPage() {
970         return mNativeContentViewCore == 0 ?
971                 false : nativeIsShowingInterstitialPage(mNativeContentViewCore);
972     }
973
974     /**
975      * Mark any new frames that have arrived since this function was last called as non-pending.
976      *
977      * @return Whether there was a pending frame from the renderer.
978      */
979     public boolean consumePendingRendererFrame() {
980         boolean hadPendingFrame = mPendingRendererFrame;
981         mPendingRendererFrame = false;
982         return hadPendingFrame;
983     }
984
985     /**
986      * @return Viewport width in physical pixels as set from onSizeChanged.
987      */
988     @CalledByNative
989     public int getViewportWidthPix() { return mViewportWidthPix; }
990
991     /**
992      * @return Viewport height in physical pixels as set from onSizeChanged.
993      */
994     @CalledByNative
995     public int getViewportHeightPix() { return mViewportHeightPix; }
996
997     /**
998      * @return Width of underlying physical surface.
999      */
1000     @CalledByNative
1001     public int getPhysicalBackingWidthPix() { return mPhysicalBackingWidthPix; }
1002
1003     /**
1004      * @return Height of underlying physical surface.
1005      */
1006     @CalledByNative
1007     public int getPhysicalBackingHeightPix() { return mPhysicalBackingHeightPix; }
1008
1009     /**
1010      * @return Amount the output surface extends past the bottom of the window viewport.
1011      */
1012     @CalledByNative
1013     public int getOverdrawBottomHeightPix() { return mOverdrawBottomHeightPix; }
1014
1015     /**
1016      * @return The amount to shrink the viewport relative to {@link #getViewportWidthPix()}.
1017      */
1018     @CalledByNative
1019     public int getViewportSizeOffsetWidthPix() { return mViewportSizeOffsetWidthPix; }
1020
1021     /**
1022      * @return The amount to shrink the viewport relative to {@link #getViewportHeightPix()}.
1023      */
1024     @CalledByNative
1025     public int getViewportSizeOffsetHeightPix() { return mViewportSizeOffsetHeightPix; }
1026
1027     /**
1028      * @see android.webkit.WebView#getContentHeight()
1029      */
1030     public float getContentHeightCss() {
1031         return mRenderCoordinates.getContentHeightCss();
1032     }
1033
1034     /**
1035      * @see android.webkit.WebView#getContentWidth()
1036      */
1037     public float getContentWidthCss() {
1038         return mRenderCoordinates.getContentWidthCss();
1039     }
1040
1041     public Bitmap getBitmap() {
1042         return getBitmap(getViewportWidthPix(), getViewportHeightPix());
1043     }
1044
1045     public Bitmap getBitmap(int width, int height) {
1046         if (width == 0 || height == 0
1047                 || getViewportWidthPix() == 0 || getViewportHeightPix() == 0) {
1048             return null;
1049         }
1050
1051         Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
1052
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);
1062             }
1063             return b;
1064         }
1065
1066         return null;
1067     }
1068
1069     /**
1070      * Generates a bitmap of the content that is performance optimized based on capture time.
1071      *
1072      * <p>
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.
1075      *
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
1080      *         will be 2).
1081      */
1082     public Pair<Bitmap, Float> getScaledPerformanceOptimizedBitmap(int width, int height) {
1083         float scale = 1f;
1084         // On tablets, always scale down to MDPI for performance reasons.
1085         if (DeviceUtils.isTablet(getContext())) {
1086             scale = getContext().getResources().getDisplayMetrics().density;
1087         }
1088         return Pair.create(
1089                 getBitmap((int) (width / scale), (int) (height / scale)),
1090                 scale);
1091     }
1092
1093     // TODO(teddchoc): Remove all these navigation controller methods from here and have the
1094     //                 embedders manage it.
1095     /**
1096      * @return Whether the current WebContents has a previous navigation entry.
1097      */
1098     public boolean canGoBack() {
1099         return mWebContents != null && mWebContents.getNavigationController().canGoBack();
1100     }
1101
1102     /**
1103      * @return Whether the current WebContents has a navigation entry after the current one.
1104      */
1105     public boolean canGoForward() {
1106         return mWebContents != null && mWebContents.getNavigationController().canGoForward();
1107     }
1108
1109     /**
1110      * @param offset The offset into the navigation history.
1111      * @return Whether we can move in history by given offset
1112      */
1113     public boolean canGoToOffset(int offset) {
1114         return mWebContents != null &&
1115                 mWebContents.getNavigationController().canGoToOffset(offset);
1116     }
1117
1118     /**
1119      * Navigates to the specified offset from the "current entry". Does nothing if the offset is out
1120      * of bounds.
1121      * @param offset The offset into the navigation history.
1122      */
1123     public void goToOffset(int offset) {
1124         if (mWebContents != null) mWebContents.getNavigationController().goToOffset(offset);
1125     }
1126
1127     @Override
1128     public void goToNavigationIndex(int index) {
1129         if (mWebContents != null) {
1130             mWebContents.getNavigationController().goToNavigationIndex(index);
1131         }
1132     }
1133
1134     /**
1135      * Goes to the navigation entry before the current one.
1136      */
1137     public void goBack() {
1138         if (mWebContents != null) mWebContents.getNavigationController().goBack();
1139     }
1140
1141     /**
1142      * Goes to the navigation entry following the current one.
1143      */
1144     public void goForward() {
1145         if (mWebContents != null) mWebContents.getNavigationController().goForward();
1146     }
1147
1148     /**
1149      * Loads the current navigation if there is a pending lazy load (after tab restore).
1150      */
1151     public void loadIfNecessary() {
1152         if (mNativeContentViewCore != 0) nativeLoadIfNecessary(mNativeContentViewCore);
1153     }
1154
1155     /**
1156      * Requests the current navigation to be loaded upon the next call to loadIfNecessary().
1157      */
1158     public void requestRestoreLoad() {
1159         if (mNativeContentViewCore != 0) nativeRequestRestoreLoad(mNativeContentViewCore);
1160     }
1161
1162     /**
1163      * Reload the current page.
1164      */
1165     public void reload(boolean checkForRepost) {
1166         mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1167         if (mNativeContentViewCore != 0) {
1168             nativeReload(mNativeContentViewCore, checkForRepost);
1169         }
1170     }
1171
1172     /**
1173      * Reload the current page, ignoring the contents of the cache.
1174      */
1175     public void reloadIgnoringCache(boolean checkForRepost) {
1176         mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1177         if (mNativeContentViewCore != 0) {
1178             nativeReloadIgnoringCache(mNativeContentViewCore, checkForRepost);
1179         }
1180     }
1181
1182     /**
1183      * Cancel the pending reload.
1184      */
1185     public void cancelPendingReload() {
1186         if (mNativeContentViewCore != 0) nativeCancelPendingReload(mNativeContentViewCore);
1187     }
1188
1189     /**
1190      * Continue the pending reload.
1191      */
1192     public void continuePendingReload() {
1193         if (mNativeContentViewCore != 0) nativeContinuePendingReload(mNativeContentViewCore);
1194     }
1195
1196     /**
1197      * Clears the ContentViewCore's page history in both the backwards and
1198      * forwards directions.
1199      */
1200     public void clearHistory() {
1201         if (mNativeContentViewCore != 0) nativeClearHistory(mNativeContentViewCore);
1202     }
1203
1204     /**
1205      * @return The selected text (empty if no text selected).
1206      */
1207     public String getSelectedText() {
1208         return mHasSelection ? mLastSelectedText : "";
1209     }
1210
1211     /**
1212      * @return Whether the current selection is editable (false if no text selected).
1213      */
1214     public boolean isSelectionEditable() {
1215         return mHasSelection ? mSelectionEditable : false;
1216     }
1217
1218     // End FrameLayout overrides.
1219
1220     /**
1221      * @see View#onTouchEvent(MotionEvent)
1222      */
1223     public boolean onTouchEvent(MotionEvent event) {
1224         undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
1225         if (!mRequestedVSyncForInput) {
1226             mRequestedVSyncForInput = true;
1227             addVSyncSubscriber();
1228         }
1229         return mContentViewGestureHandler.onTouchEvent(event);
1230     }
1231
1232     /** @see ContentViewGestureHandler#setIgnoreRemainingTouchEvents */
1233     public void setIgnoreRemainingTouchEvents() {
1234         mContentViewGestureHandler.setIgnoreRemainingTouchEvents();
1235     }
1236
1237     @SuppressWarnings("unused")
1238     @CalledByNative
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());
1245         }
1246     }
1247
1248     @SuppressWarnings("unused")
1249     @CalledByNative
1250     private void onFlingStartEventHadNoConsumer(int vx, int vy) {
1251         for (mGestureStateListenersIterator.rewind();
1252                     mGestureStateListenersIterator.hasNext();) {
1253             mGestureStateListenersIterator.next().onUnhandledFlingStartEvent(vx, vy);
1254         }
1255     }
1256
1257     @SuppressWarnings("unused")
1258     @CalledByNative
1259     private void onFlingCancelEventAck() {
1260         updateGestureStateListener(GestureEventType.FLING_CANCEL);
1261     }
1262
1263     @SuppressWarnings("unused")
1264     @CalledByNative
1265     private void onScrollBeginEventAck() {
1266         temporarilyHideTextHandles();
1267         mZoomControlsDelegate.invokeZoomPicker();
1268         updateGestureStateListener(GestureEventType.SCROLL_START);
1269     }
1270
1271     @SuppressWarnings("unused")
1272     @CalledByNative
1273     private void onScrollUpdateGestureConsumed() {
1274         mZoomControlsDelegate.invokeZoomPicker();
1275         for (mGestureStateListenersIterator.rewind();
1276                 mGestureStateListenersIterator.hasNext();) {
1277             mGestureStateListenersIterator.next().onScrollUpdateGestureConsumed();
1278         }
1279     }
1280
1281     @SuppressWarnings("unused")
1282     @CalledByNative
1283     private void onScrollEndEventAck() {
1284         updateGestureStateListener(GestureEventType.SCROLL_END);
1285     }
1286
1287     @SuppressWarnings("unused")
1288     @CalledByNative
1289     private void onPinchBeginEventAck() {
1290         temporarilyHideTextHandles();
1291         updateGestureStateListener(GestureEventType.PINCH_BEGIN);
1292     }
1293
1294     @SuppressWarnings("unused")
1295     @CalledByNative
1296     private void onPinchEndEventAck() {
1297         updateGestureStateListener(GestureEventType.PINCH_END);
1298     }
1299
1300     @SuppressWarnings("unused")
1301     @CalledByNative
1302     private void onDoubleTapEventAck() {
1303         temporarilyHideTextHandles();
1304     }
1305
1306     /**
1307      * Called just prior to a tap or press gesture being forwarded to the renderer.
1308      */
1309     @SuppressWarnings("unused")
1310     @CalledByNative
1311     private boolean filterTapOrPressEvent(int type, int x, int y) {
1312         if (type == GestureEventType.LONG_PRESS && offerLongPressToEmbedder()) {
1313             return true;
1314         }
1315         updateForTapOrPress(type, x, y);
1316         updateForDoubleTapUMA(type);
1317         return false;
1318     }
1319
1320     @Override
1321     public void onTouchEventHandlingBegin(MotionEvent event) {
1322         if (mNativeContentViewCore == 0) return;
1323         nativeOnTouchEventHandlingBegin(mNativeContentViewCore,event);
1324     }
1325
1326     @Override
1327     public void onTouchEventHandlingEnd() {
1328         if (mNativeContentViewCore == 0) return;
1329         nativeOnTouchEventHandlingEnd(mNativeContentViewCore);
1330     }
1331
1332     /**
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)}.
1336      */
1337     @Override
1338     public boolean onGestureEventCreated(int type, long timeMs, int x, int y, Bundle b) {
1339         if (mNativeContentViewCore == 0) return false;
1340         switch (type) {
1341             case GestureEventType.SHOW_PRESS:
1342                 nativeShowPress(mNativeContentViewCore, timeMs, x, y);
1343                 return true;
1344             case GestureEventType.TAP_CANCEL:
1345                 nativeTapCancel(mNativeContentViewCore, timeMs, x, y);
1346                 return true;
1347             case GestureEventType.TAP_DOWN:
1348                 nativeTapDown(mNativeContentViewCore, timeMs, x, y);
1349                 return true;
1350             case GestureEventType.DOUBLE_TAP:
1351                 nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1352                 return true;
1353             case GestureEventType.SINGLE_TAP_UP:
1354                 nativeSingleTap(mNativeContentViewCore, timeMs, x, y, false);
1355                 return true;
1356             case GestureEventType.SINGLE_TAP_CONFIRMED:
1357                 if (!b.getBoolean(ContentViewGestureHandler.SHOW_PRESS, false)) {
1358                     nativeShowPress(mNativeContentViewCore, timeMs, x, y);
1359                 }
1360                 nativeSingleTap(mNativeContentViewCore, timeMs, x, y, false);
1361                 return true;
1362             case GestureEventType.SINGLE_TAP_UNCONFIRMED:
1363                 nativeSingleTapUnconfirmed(mNativeContentViewCore, timeMs, x, y);
1364                 return true;
1365             case GestureEventType.LONG_PRESS:
1366                 nativeLongPress(mNativeContentViewCore, timeMs, x, y, false);
1367                 return true;
1368             case GestureEventType.LONG_TAP:
1369                 nativeLongTap(mNativeContentViewCore, timeMs, x, y, false);
1370                 return true;
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);
1375                 return true;
1376             }
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);
1381                 return true;
1382             }
1383             case GestureEventType.SCROLL_END:
1384                 nativeScrollEnd(mNativeContentViewCore, timeMs);
1385                 return true;
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));
1390                 return true;
1391             case GestureEventType.FLING_CANCEL:
1392                 nativeFlingCancel(mNativeContentViewCore, timeMs);
1393                 return true;
1394             case GestureEventType.PINCH_BEGIN:
1395                 nativePinchBegin(mNativeContentViewCore, timeMs, x, y);
1396                 return true;
1397             case GestureEventType.PINCH_BY:
1398                 nativePinchBy(mNativeContentViewCore, timeMs, x, y,
1399                         b.getFloat(ContentViewGestureHandler.DELTA, 0));
1400                 return true;
1401             case GestureEventType.PINCH_END:
1402                 nativePinchEnd(mNativeContentViewCore, timeMs);
1403                 return true;
1404             default:
1405                 return false;
1406         }
1407     }
1408
1409     @VisibleForTesting
1410     public void sendDoubleTapForTest(long timeMs, int x, int y) {
1411         if (mNativeContentViewCore == 0) return;
1412         nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1413     }
1414
1415     @VisibleForTesting
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);
1421     }
1422
1423     /**
1424      * Add a listener that gets alerted on gesture state changes.
1425      * @param listener Listener to add.
1426      */
1427     public void addGestureStateListener(GestureStateListener listener) {
1428         mGestureStateListeners.addObserver(listener);
1429     }
1430
1431     /**
1432      * Removes a listener that was added to watch for gesture state changes.
1433      * @param listener Listener to remove.
1434      */
1435     public void removeGestureStateListener(GestureStateListener listener) {
1436         mGestureStateListeners.removeObserver(listener);
1437     }
1438
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();
1446                     break;
1447                 case GestureEventType.PINCH_END:
1448                     listener.onPinchEnded();
1449                     break;
1450                 case GestureEventType.FLING_END:
1451                     listener.onFlingEndGesture(
1452                             computeVerticalScrollOffset(),
1453                             computeVerticalScrollExtent());
1454                     break;
1455                 case GestureEventType.FLING_CANCEL:
1456                     listener.onFlingCancelGesture();
1457                     break;
1458                 case GestureEventType.SCROLL_START:
1459                     listener.onScrollStarted(
1460                             computeVerticalScrollOffset(),
1461                             computeVerticalScrollExtent());
1462                     break;
1463                 case GestureEventType.SCROLL_END:
1464                     listener.onScrollEnded(
1465                             computeVerticalScrollOffset(),
1466                             computeVerticalScrollExtent());
1467                     break;
1468                 default:
1469                     break;
1470             }
1471         }
1472     }
1473
1474     /** Callback interface for evaluateJavaScript(). */
1475     public interface JavaScriptCallback {
1476         void handleJavaScriptResult(String jsonResult);
1477     }
1478
1479     /**
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.
1483      *
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.
1489      */
1490     public void evaluateJavaScript(String script, JavaScriptCallback callback) {
1491         if (mNativeContentViewCore == 0) return;
1492         nativeEvaluateJavaScript(mNativeContentViewCore, script, callback, false);
1493     }
1494
1495     /**
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.
1498      *
1499      * @param script The Javascript to execute.
1500      */
1501     public void evaluateJavaScriptEvenIfNotYetNavigated(String script) {
1502         if (mNativeContentViewCore == 0) return;
1503         nativeEvaluateJavaScript(mNativeContentViewCore, script, null, true);
1504     }
1505
1506     /**
1507      * To be called when the ContentView is shown.
1508      */
1509     public void onShow() {
1510         assert mNativeContentViewCore != 0;
1511         if (!mInForeground) {
1512             int pid = nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1513             ChildProcessLauncher.getBindingManager().setInForeground(pid, true);
1514         }
1515         mInForeground = true;
1516         nativeOnShow(mNativeContentViewCore);
1517         setAccessibilityState(mAccessibilityManager.isEnabled());
1518     }
1519
1520     /**
1521      * To be called when the ContentView is hidden.
1522      */
1523     public void onHide() {
1524         assert mNativeContentViewCore != 0;
1525         if (mInForeground) {
1526             int pid = nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1527             ChildProcessLauncher.getBindingManager().setInForeground(pid, false);
1528         }
1529         mInForeground = false;
1530         hidePopupDialog();
1531         setInjectedAccessibility(false);
1532         nativeOnHide(mNativeContentViewCore);
1533     }
1534
1535     /**
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.
1540      */
1541     public ContentSettings getContentSettings() {
1542         return mContentSettings;
1543     }
1544
1545     private void onRenderCoordinatesUpdated() {
1546         if (mContentViewGestureHandler == null) return;
1547
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());
1555     }
1556
1557     private void hidePopupDialog() {
1558         SelectPopupDialog.hide(this);
1559         hideHandles();
1560         hideSelectActionBar();
1561     }
1562
1563     void hideSelectActionBar() {
1564         if (mActionMode != null) {
1565             mActionMode.finish();
1566             mActionMode = null;
1567         }
1568     }
1569
1570     public boolean isSelectActionBarShowing() {
1571         return mActionMode != null;
1572     }
1573
1574     private void resetGestureDetectors() {
1575         mContentViewGestureHandler.resetGestureHandlers();
1576     }
1577
1578     /**
1579      * @see View#onAttachedToWindow()
1580      */
1581     @SuppressWarnings("javadoc")
1582     public void onAttachedToWindow() {
1583         setAccessibilityState(mAccessibilityManager.isEnabled());
1584     }
1585
1586     /**
1587      * @see View#onDetachedFromWindow()
1588      */
1589     @SuppressWarnings("javadoc")
1590     public void onDetachedFromWindow() {
1591         setInjectedAccessibility(false);
1592         hidePopupDialog();
1593         mZoomControlsDelegate.dismissZoomPicker();
1594         unregisterAccessibilityContentObserver();
1595     }
1596
1597     /**
1598      * @see View#onVisibilityChanged(android.view.View, int)
1599      */
1600     public void onVisibilityChanged(View changedView, int visibility) {
1601         if (visibility != View.VISIBLE) {
1602             mZoomControlsDelegate.dismissZoomPicker();
1603         }
1604     }
1605
1606     /**
1607      * @see View#onCreateInputConnection(EditorInfo)
1608      */
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;
1615         }
1616         mInputConnection =
1617                 mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter, outAttrs);
1618         return mInputConnection;
1619     }
1620
1621     public Editable getEditableForTest() {
1622         return mInputConnection.getEditable();
1623     }
1624
1625     /**
1626      * @see View#onCheckIsTextEditor()
1627      */
1628     public boolean onCheckIsTextEditor() {
1629         return mImeAdapter.hasTextInputType();
1630     }
1631
1632     /**
1633      * @see View#onConfigurationChanged(Configuration)
1634      */
1635     @SuppressWarnings("javadoc")
1636     public void onConfigurationChanged(Configuration newConfig) {
1637         TraceEvent.begin();
1638
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);
1647         }
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() {
1651             @Override
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();
1656             }
1657         });
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();
1661         TraceEvent.end();
1662     }
1663
1664     /**
1665      * @see View#onSizeChanged(int, int, int, int)
1666      */
1667     @SuppressWarnings("javadoc")
1668     public void onSizeChanged(int wPix, int hPix, int owPix, int ohPix) {
1669         if (getViewportWidthPix() == wPix && getViewportHeightPix() == hPix) return;
1670
1671         mViewportWidthPix = wPix;
1672         mViewportHeightPix = hPix;
1673         if (mNativeContentViewCore != 0) {
1674             nativeWasResized(mNativeContentViewCore);
1675         }
1676
1677         updateAfterSizeChanged();
1678     }
1679
1680     /**
1681      * Called when the ContentView's position in the activity window changed. This information is
1682      * used for cropping screenshots.
1683      */
1684     public void onLocationInWindowChanged(int x, int y) {
1685         mLocationInWindowX = x;
1686         mLocationInWindowY = y;
1687     }
1688
1689     /**
1690      * Called when the underlying surface the compositor draws to changes size.
1691      * This may be larger than the viewport size.
1692      */
1693     public void onPhysicalBackingSizeChanged(int wPix, int hPix) {
1694         if (mPhysicalBackingWidthPix == wPix && mPhysicalBackingHeightPix == hPix) return;
1695
1696         mPhysicalBackingWidthPix = wPix;
1697         mPhysicalBackingHeightPix = hPix;
1698
1699         if (mNativeContentViewCore != 0) {
1700             nativeWasResized(mNativeContentViewCore);
1701         }
1702     }
1703
1704     /**
1705      * Called when the amount the surface is overdrawing off the bottom has changed.
1706      * @param overdrawHeightPix The overdraw height.
1707      */
1708     public void onOverdrawBottomHeightChanged(int overdrawHeightPix) {
1709         if (mOverdrawBottomHeightPix == overdrawHeightPix) return;
1710
1711         mOverdrawBottomHeightPix = overdrawHeightPix;
1712
1713         if (mNativeContentViewCore != 0) {
1714             nativeWasResized(mNativeContentViewCore);
1715         }
1716     }
1717
1718     private void updateAfterSizeChanged() {
1719         mPopupZoomer.hide(false);
1720
1721         // Execute a delayed form focus operation because the OSK was brought
1722         // up earlier.
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();
1730                 }
1731                 mFocusPreOSKViewportRect.setEmpty();
1732             }
1733         } else if (mUnfocusOnNextSizeChanged) {
1734             undoScrollFocusedEditableNodeIntoViewIfNeeded(true);
1735             mUnfocusOnNextSizeChanged = false;
1736         }
1737     }
1738
1739     private void scrollFocusedEditableNodeIntoView() {
1740         if (mNativeContentViewCore != 0) {
1741             Runnable scrollTask = new Runnable() {
1742                 @Override
1743                 public void run() {
1744                     if (mNativeContentViewCore != 0) {
1745                         nativeScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1746                     }
1747                 }
1748             };
1749
1750             scrollTask.run();
1751
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;
1756         }
1757     }
1758
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
1764         // an undo.
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() {
1770                 @Override
1771                 public void run() {
1772                     if (mNativeContentViewCore != 0) {
1773                         nativeUndoScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1774                     }
1775                 }
1776             };
1777
1778             scrollTask.run();
1779         }
1780         mScrolledAndZoomedFocusedEditableNode = false;
1781     }
1782
1783     /**
1784      * @see View#onWindowFocusChanged(boolean)
1785      */
1786     public void onWindowFocusChanged(boolean hasWindowFocus) {
1787         if (!hasWindowFocus) {
1788             mContentViewGestureHandler.onWindowFocusLost();
1789         }
1790     }
1791
1792     public void onFocusChanged(boolean gainFocus) {
1793         if (!gainFocus) getContentViewClient().onImeStateChangeRequested(false);
1794         if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1795     }
1796
1797     /**
1798      * @see View#onKeyUp(int, KeyEvent)
1799      */
1800     public boolean onKeyUp(int keyCode, KeyEvent event) {
1801         if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1802             mPopupZoomer.hide(true);
1803             return true;
1804         }
1805         return mContainerViewInternals.super_onKeyUp(keyCode, event);
1806     }
1807
1808     /**
1809      * @see View#dispatchKeyEventPreIme(KeyEvent)
1810      */
1811     public boolean dispatchKeyEventPreIme(KeyEvent event) {
1812         try {
1813             TraceEvent.begin();
1814             if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && mImeAdapter.isActive()) {
1815                 mUnfocusOnNextSizeChanged = true;
1816             } else {
1817                 undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
1818             }
1819             return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1820         } finally {
1821             TraceEvent.end();
1822         }
1823     }
1824
1825     /**
1826      * @see View#dispatchKeyEvent(KeyEvent)
1827      */
1828     public boolean dispatchKeyEvent(KeyEvent event) {
1829         if (getContentViewClient().shouldOverrideKeyEvent(event)) {
1830             return mContainerViewInternals.super_dispatchKeyEvent(event);
1831         }
1832
1833         if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
1834             showImeIfNeeded();
1835             // Event is not consumed here, because ImeAdapter might interpret
1836             // it as "Enter".
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
1842         }
1843
1844         if (mImeAdapter.dispatchKeyEvent(event)) return true;
1845
1846         return mContainerViewInternals.super_dispatchKeyEvent(event);
1847     }
1848
1849     /**
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.
1854      */
1855     public boolean onHoverEvent(MotionEvent event) {
1856         TraceEvent.begin("onHoverEvent");
1857         mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1858         if (mBrowserAccessibilityManager != null) {
1859             return mBrowserAccessibilityManager.onHoverEvent(event);
1860         }
1861         if (mNativeContentViewCore != 0) {
1862             nativeSendMouseMoveEvent(mNativeContentViewCore, event.getEventTime(),
1863                     event.getX(), event.getY());
1864         }
1865         TraceEvent.end("onHoverEvent");
1866         return true;
1867     }
1868
1869     /**
1870      * @see View#onGenericMotionEvent(MotionEvent)
1871      */
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));
1879
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() {
1885                         @Override
1886                         public void run() {
1887                             onHoverEvent(eventFakeMouseMove);
1888                         }
1889                     };
1890                     mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1891                     return true;
1892             }
1893         }
1894         return mContainerViewInternals.super_onGenericMotionEvent(event);
1895     }
1896
1897     /**
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.
1903      */
1904     public void scrollBy(int xPix, int yPix) {
1905         if (mNativeContentViewCore != 0) {
1906             nativeScrollBy(mNativeContentViewCore,
1907                     System.currentTimeMillis(), 0, 0, xPix, yPix);
1908         }
1909     }
1910
1911     /**
1912      * @see View#scrollTo(int, int)
1913      */
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);
1927         }
1928     }
1929
1930     // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1931     //       see: b/6029133
1932     public int getNativeScrollXForTest() {
1933         return mRenderCoordinates.getScrollXPixInt();
1934     }
1935
1936     // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1937     //       see: b/6029133
1938     public int getNativeScrollYForTest() {
1939         return mRenderCoordinates.getScrollYPixInt();
1940     }
1941
1942     /**
1943      * @see View#computeHorizontalScrollExtent()
1944      */
1945     @SuppressWarnings("javadoc")
1946     public int computeHorizontalScrollExtent() {
1947         return mRenderCoordinates.getLastFrameViewportWidthPixInt();
1948     }
1949
1950     /**
1951      * @see View#computeHorizontalScrollOffset()
1952      */
1953     @SuppressWarnings("javadoc")
1954     public int computeHorizontalScrollOffset() {
1955         return mRenderCoordinates.getScrollXPixInt();
1956     }
1957
1958     /**
1959      * @see View#computeHorizontalScrollRange()
1960      */
1961     @SuppressWarnings("javadoc")
1962     public int computeHorizontalScrollRange() {
1963         return mRenderCoordinates.getContentWidthPixInt();
1964     }
1965
1966     /**
1967      * @see View#computeVerticalScrollExtent()
1968      */
1969     @SuppressWarnings("javadoc")
1970     public int computeVerticalScrollExtent() {
1971         return mRenderCoordinates.getLastFrameViewportHeightPixInt();
1972     }
1973
1974     /**
1975      * @see View#computeVerticalScrollOffset()
1976      */
1977     @SuppressWarnings("javadoc")
1978     public int computeVerticalScrollOffset() {
1979         return mRenderCoordinates.getScrollYPixInt();
1980     }
1981
1982     /**
1983      * @see View#computeVerticalScrollRange()
1984      */
1985     @SuppressWarnings("javadoc")
1986     public int computeVerticalScrollRange() {
1987         return mRenderCoordinates.getContentHeightPixInt();
1988     }
1989
1990     // End FrameLayout overrides.
1991
1992     /**
1993      * @see View#awakenScrollBars(int, boolean)
1994      */
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) {
2001             return false;
2002         } else {
2003             return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
2004         }
2005     }
2006
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) {
2012             return;
2013         }
2014
2015         if (mContainerView.isFocusable() && mContainerView.isFocusableInTouchMode()
2016                 && !mContainerView.isFocused())  {
2017             mContainerView.requestFocus();
2018         }
2019
2020         if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
2021
2022         if (type == GestureEventType.LONG_PRESS
2023                 || type == GestureEventType.LONG_TAP) {
2024             getInsertionHandleController().allowAutomaticShowing();
2025             getSelectionHandleController().allowAutomaticShowing();
2026         } else {
2027             setClickXAndY((int) xPix, (int) yPix);
2028             if (mSelectionEditable) getInsertionHandleController().allowAutomaticShowing();
2029         }
2030     }
2031
2032     private void setClickXAndY(int x, int y) {
2033         mSingleTapX = x;
2034         mSingleTapY = y;
2035     }
2036
2037     /**
2038      * @return The x coordinate for the last point that a singleTap gesture was initiated from.
2039      */
2040     public int getSingleTapX()  {
2041         return mSingleTapX;
2042     }
2043
2044     /**
2045      * @return The y coordinate for the last point that a singleTap gesture was initiated from.
2046      */
2047     public int getSingleTapY()  {
2048         return mSingleTapY;
2049     }
2050
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;
2055
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;
2061         }
2062     }
2063
2064     private void updateForDoubleTapUMA(int type) {
2065         updateDoubleTapUmaTimer();
2066
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
2073             // the statistics.
2074             if (mLastDoubleTapTimeMs > 0) {
2075                 sendActionAfterDoubleTapUMA(UMAActionAfterDoubleTap.NO_ACTION);
2076             }
2077
2078             mLastDoubleTapTimeMs = SystemClock.uptimeMillis();
2079         }
2080     }
2081
2082     private void reportActionAfterDoubleTapUMA(int type) {
2083         updateDoubleTapUmaTimer();
2084
2085         if (mLastDoubleTapTimeMs == 0) return;
2086
2087         long nowMs = SystemClock.uptimeMillis();
2088         if ((nowMs - mLastDoubleTapTimeMs) < ACTION_AFTER_DOUBLE_TAP_WINDOW_MS) {
2089             sendActionAfterDoubleTapUMA(type);
2090             mLastDoubleTapTimeMs = 0;
2091         }
2092     }
2093
2094     private void sendSingleTapUMA(int type) {
2095         if (mNativeContentViewCore == 0) return;
2096         nativeSendSingleTapUma(
2097                 mNativeContentViewCore,
2098                 type,
2099                 UMASingleTapType.COUNT);
2100     }
2101
2102     private void sendActionAfterDoubleTapUMA(int type) {
2103         if (mNativeContentViewCore == 0) return;
2104         nativeSendActionAfterDoubleTapUma(
2105                 mNativeContentViewCore,
2106                 type,
2107                 !mContentViewGestureHandler.isClickDelayDisabled(),
2108                 UMAActionAfterDoubleTap.COUNT);
2109     }
2110
2111     public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
2112         mZoomControlsDelegate = zoomControlsDelegate;
2113     }
2114
2115     public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
2116         mContentViewGestureHandler.updateMultiTouchSupport(supportsMultiTouchZoom);
2117     }
2118
2119     public void updateDoubleTapSupport(boolean supportsDoubleTap) {
2120         mContentViewGestureHandler.updateDoubleTapSupport(supportsDoubleTap);
2121     }
2122
2123     public void selectPopupMenuItems(int[] indices) {
2124         if (mNativeContentViewCore != 0) {
2125             nativeSelectPopupMenuItems(mNativeContentViewCore, indices);
2126         }
2127     }
2128
2129     /**
2130      * Get the screen orientation from the OS and push it to WebKit.
2131      *
2132      * TODO(husky): Add a hook for mock orientations.
2133      */
2134     private void sendOrientationChangeEvent() {
2135         if (mNativeContentViewCore == 0) return;
2136
2137         WindowManager windowManager =
2138                 (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
2139         switch (windowManager.getDefaultDisplay().getRotation()) {
2140             case Surface.ROTATION_90:
2141                 nativeSendOrientationChangeEvent(mNativeContentViewCore, 90);
2142                 break;
2143             case Surface.ROTATION_180:
2144                 nativeSendOrientationChangeEvent(mNativeContentViewCore, 180);
2145                 break;
2146             case Surface.ROTATION_270:
2147                 nativeSendOrientationChangeEvent(mNativeContentViewCore, -90);
2148                 break;
2149             case Surface.ROTATION_0:
2150                 nativeSendOrientationChangeEvent(mNativeContentViewCore, 0);
2151                 break;
2152             default:
2153                 Log.w(TAG, "Unknown rotation!");
2154                 break;
2155         }
2156     }
2157
2158     /**
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.
2163      */
2164     public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
2165         mDownloadDelegate = delegate;
2166     }
2167
2168     // Called by DownloadController.
2169     ContentViewDownloadDelegate getDownloadDelegate() {
2170         return mDownloadDelegate;
2171     }
2172
2173     private SelectionHandleController getSelectionHandleController() {
2174         if (mSelectionHandleController == null) {
2175             mSelectionHandleController = new SelectionHandleController(
2176                     getContainerView(), mPositionObserver) {
2177                 @Override
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());
2183                     }
2184                 }
2185
2186                 @Override
2187                 public void showHandles(int startDir, int endDir) {
2188                     super.showHandles(startDir, endDir);
2189                     showSelectActionBar();
2190                 }
2191
2192             };
2193
2194             mSelectionHandleController.hideAndDisallowAutomaticShowing();
2195         }
2196
2197         return mSelectionHandleController;
2198     }
2199
2200     private InsertionHandleController getInsertionHandleController() {
2201         if (mInsertionHandleController == null) {
2202             mInsertionHandleController = new InsertionHandleController(
2203                     getContainerView(), mPositionObserver) {
2204                 private static final int AVERAGE_LINE_HEIGHT = 14;
2205
2206                 @Override
2207                 public void setCursorPosition(int x, int y) {
2208                     if (mNativeContentViewCore != 0) {
2209                         nativeMoveCaret(mNativeContentViewCore,
2210                                 x, y - mRenderCoordinates.getContentOffsetYPix());
2211                     }
2212                 }
2213
2214                 @Override
2215                 public void paste() {
2216                     mImeAdapter.paste();
2217                     hideHandles();
2218                 }
2219
2220                 @Override
2221                 public int getLineHeight() {
2222                     return (int) Math.ceil(
2223                             mRenderCoordinates.fromLocalCssToPix(AVERAGE_LINE_HEIGHT));
2224                 }
2225
2226                 @Override
2227                 public void showHandle() {
2228                     super.showHandle();
2229                 }
2230             };
2231
2232             mInsertionHandleController.hideAndDisallowAutomaticShowing();
2233         }
2234
2235         return mInsertionHandleController;
2236     }
2237
2238     @VisibleForTesting
2239     public InsertionHandleController getInsertionHandleControllerForTest() {
2240         return mInsertionHandleController;
2241     }
2242
2243     @VisibleForTesting
2244     public SelectionHandleController getSelectionHandleControllerForTest() {
2245         return mSelectionHandleController;
2246     }
2247
2248     private void updateHandleScreenPositions() {
2249         if (isSelectionHandleShowing()) {
2250             mSelectionHandleController.setStartHandlePosition(
2251                     mStartHandlePoint.getXPix(), mStartHandlePoint.getYPix());
2252             mSelectionHandleController.setEndHandlePosition(
2253                     mEndHandlePoint.getXPix(), mEndHandlePoint.getYPix());
2254         }
2255
2256         if (isInsertionHandleShowing()) {
2257             mInsertionHandleController.setHandlePosition(
2258                     mInsertionHandlePoint.getXPix(), mInsertionHandlePoint.getYPix());
2259         }
2260     }
2261
2262     private void hideHandles() {
2263         if (mSelectionHandleController != null) {
2264             mSelectionHandleController.hideAndDisallowAutomaticShowing();
2265         }
2266         if (mInsertionHandleController != null) {
2267             mInsertionHandleController.hideAndDisallowAutomaticShowing();
2268         }
2269         mPositionObserver.removeListener(mPositionListener);
2270     }
2271
2272     private void showSelectActionBar() {
2273         if (mActionMode != null) {
2274             mActionMode.invalidate();
2275             return;
2276         }
2277
2278         // Start a new action mode with a SelectActionModeCallback.
2279         SelectActionModeCallback.ActionHandler actionHandler =
2280                 new SelectActionModeCallback.ActionHandler() {
2281             @Override
2282             public void selectAll() {
2283                 mImeAdapter.selectAll();
2284             }
2285
2286             @Override
2287             public void cut() {
2288                 mImeAdapter.cut();
2289             }
2290
2291             @Override
2292             public void copy() {
2293                 mImeAdapter.copy();
2294             }
2295
2296             @Override
2297             public void paste() {
2298                 mImeAdapter.paste();
2299             }
2300
2301             @Override
2302             public void share() {
2303                 final String query = getSelectedText();
2304                 if (TextUtils.isEmpty(query)) return;
2305
2306                 Intent send = new Intent(Intent.ACTION_SEND);
2307                 send.setType("text/plain");
2308                 send.putExtra(Intent.EXTRA_TEXT, query);
2309                 try {
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.
2316                 }
2317             }
2318
2319             @Override
2320             public void search() {
2321                 final String query = getSelectedText();
2322                 if (TextUtils.isEmpty(query)) return;
2323
2324                 // See if ContentViewClient wants to override
2325                 if (getContentViewClient().doesPerformWebSearch()) {
2326                     getContentViewClient().performWebSearch(query);
2327                     return;
2328                 }
2329
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);
2336                 }
2337                 try {
2338                     getContext().startActivity(i);
2339                 } catch (android.content.ActivityNotFoundException ex) {
2340                     // If no app handles it, do nothing.
2341                 }
2342             }
2343
2344             @Override
2345             public boolean isSelectionEditable() {
2346                 return mSelectionEditable;
2347             }
2348
2349             @Override
2350             public void onDestroyActionMode() {
2351                 mActionMode = null;
2352                 if (mUnselectAllOnActionModeDismiss) mImeAdapter.unselect();
2353                 getContentViewClient().onContextualActionBarHidden();
2354             }
2355
2356             @Override
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;
2362             }
2363
2364             @Override
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;
2371             }
2372         };
2373         mActionMode = null;
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)));
2379         }
2380         mUnselectAllOnActionModeDismiss = true;
2381         if (mActionMode == null) {
2382             // There is no ActionMode, so remove the selection.
2383             mImeAdapter.unselect();
2384         } else {
2385             getContentViewClient().onContextualActionBarShown();
2386         }
2387     }
2388
2389     public boolean getUseDesktopUserAgent() {
2390         if (mNativeContentViewCore != 0) {
2391             return nativeGetUseDesktopUserAgent(mNativeContentViewCore);
2392         }
2393         return false;
2394     }
2395
2396     /**
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.
2400      */
2401     public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange) {
2402         if (mNativeContentViewCore != 0) {
2403             nativeSetUseDesktopUserAgent(mNativeContentViewCore, override, reloadOnChange);
2404         }
2405     }
2406
2407     public void clearSslPreferences() {
2408         nativeClearSslPreferences(mNativeContentViewCore);
2409     }
2410
2411     private boolean isSelectionHandleShowing() {
2412         return mSelectionHandleController != null && mSelectionHandleController.isShowing();
2413     }
2414
2415     private boolean isInsertionHandleShowing() {
2416         return mInsertionHandleController != null && mInsertionHandleController.isShowing();
2417     }
2418
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);
2424         }
2425         if (isInsertionHandleShowing() && !mInsertionHandleController.isDragging()) {
2426             mInsertionHandleController.setHandleVisibility(HandleView.INVISIBLE);
2427         }
2428         scheduleTextHandleFadeIn();
2429     }
2430
2431     private boolean allowTextHandleFadeIn() {
2432         if (mContentViewGestureHandler.isNativeScrolling() ||
2433                 mContentViewGestureHandler.isNativePinching()) {
2434             return false;
2435         }
2436
2437         if (mPopupZoomer.isShowing()) return false;
2438
2439         return true;
2440     }
2441
2442     // Cancels any pending fade in and schedules a new one.
2443     private void scheduleTextHandleFadeIn() {
2444         if (!isInsertionHandleShowing() && !isSelectionHandleShowing()) return;
2445
2446         if (mDeferredHandleFadeInRunnable == null) {
2447             mDeferredHandleFadeInRunnable = new Runnable() {
2448                 @Override
2449                 public void run() {
2450                     if (!allowTextHandleFadeIn()) {
2451                         // Delay fade in until it is allowed.
2452                         scheduleTextHandleFadeIn();
2453                     } else {
2454                         if (isSelectionHandleShowing()) {
2455                             mSelectionHandleController.beginHandleFadeIn();
2456                         }
2457                         if (isInsertionHandleShowing()) {
2458                             mInsertionHandleController.beginHandleFadeIn();
2459                         }
2460                     }
2461                 }
2462             };
2463         }
2464
2465         mContainerView.removeCallbacks(mDeferredHandleFadeInRunnable);
2466         mContainerView.postDelayed(mDeferredHandleFadeInRunnable, TEXT_HANDLE_FADE_IN_DELAY);
2467     }
2468
2469     /**
2470      * Shows the IME if the focused widget could accept text input.
2471      */
2472     public void showImeIfNeeded() {
2473         if (mNativeContentViewCore != 0) nativeShowImeIfNeeded(mNativeContentViewCore);
2474     }
2475
2476     @SuppressWarnings("unused")
2477     @CalledByNative
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));
2492
2493         final float contentOffsetYPix = mRenderCoordinates.fromDipToPix(contentOffsetYCss);
2494
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 =
2504                 pageScaleChanged
2505                 || scrollOffsetX != mRenderCoordinates.getScrollX()
2506                 || scrollOffsetY != mRenderCoordinates.getScrollY();
2507         final boolean contentOffsetChanged =
2508                 contentOffsetYPix != mRenderCoordinates.getContentOffsetYPix();
2509
2510         final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
2511         final boolean needUpdateZoomControls = scaleLimitsChanged || scrollChanged;
2512         final boolean needTemporarilyHideHandles = scrollChanged;
2513
2514         if (needHidePopupZoomer) mPopupZoomer.hide(true);
2515
2516         if (scrollChanged) {
2517             mContainerViewInternals.onScrollChanged(
2518                     (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetX),
2519                     (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetY),
2520                     (int) mRenderCoordinates.getScrollXPix(),
2521                     (int) mRenderCoordinates.getScrollYPix());
2522         }
2523
2524         mRenderCoordinates.updateFrameInfo(
2525                 scrollOffsetX, scrollOffsetY,
2526                 contentWidth, contentHeight,
2527                 viewportWidth, viewportHeight,
2528                 pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
2529                 contentOffsetYPix);
2530         onRenderCoordinatesUpdated();
2531
2532         if (scrollChanged || contentOffsetChanged) {
2533             for (mGestureStateListenersIterator.rewind();
2534                     mGestureStateListenersIterator.hasNext();) {
2535                 mGestureStateListenersIterator.next().onScrollOffsetOrExtentChanged(
2536                         computeVerticalScrollOffset(),
2537                         computeVerticalScrollExtent());
2538             }
2539         }
2540
2541         if (needTemporarilyHideHandles) temporarilyHideTextHandles();
2542         if (needUpdateZoomControls) mZoomControlsDelegate.updateZoomControls();
2543         if (contentOffsetChanged) updateHandleScreenPositions();
2544
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);
2551
2552         mPendingRendererFrame = true;
2553         if (mBrowserAccessibilityManager != null) {
2554             mBrowserAccessibilityManager.notifyFrameInfoInitialized();
2555         }
2556
2557         // Update geometry for external video surface.
2558         getContentViewClient().onGeometryChanged(-1, null);
2559     }
2560
2561     @CalledByNative
2562     private void updateImeAdapter(int nativeImeAdapterAndroid, int textInputType,
2563             String text, int selectionStart, int selectionEnd,
2564             int compositionStart, int compositionEnd, boolean showImeIfNeeded, boolean requireAck) {
2565         TraceEvent.begin();
2566         mSelectionEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
2567
2568         if (mActionMode != null) mActionMode.invalidate();
2569
2570         mImeAdapter.attachAndShowIfNeeded(nativeImeAdapterAndroid, textInputType,
2571                 selectionStart, selectionEnd, showImeIfNeeded);
2572
2573         if (mInputConnection != null) {
2574             mInputConnection.updateState(text, selectionStart, selectionEnd, compositionStart,
2575                     compositionEnd, requireAck);
2576         }
2577         TraceEvent.end();
2578     }
2579
2580     @SuppressWarnings("unused")
2581     @CalledByNative
2582     private void setTitle(String title) {
2583         getContentViewClient().onUpdateTitle(title);
2584     }
2585
2586     /**
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.
2592      */
2593     @SuppressWarnings("unused")
2594     @CalledByNative
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]));
2601         }
2602         SelectPopupDialog.show(this, popupItems, multiple, selectedIndices);
2603     }
2604
2605     @SuppressWarnings("unused")
2606     @CalledByNative
2607     private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
2608         mPopupZoomer.setBitmap(zoomedBitmap);
2609         mPopupZoomer.show(targetRect);
2610         temporarilyHideTextHandles();
2611     }
2612
2613     @SuppressWarnings("unused")
2614     @CalledByNative
2615     private TouchEventSynthesizer createTouchEventSynthesizer() {
2616         return new TouchEventSynthesizer(this);
2617     }
2618
2619     @SuppressWarnings("unused")
2620     @CalledByNative
2621     private void onSelectionChanged(String text) {
2622         mLastSelectedText = text;
2623     }
2624
2625     @SuppressWarnings("unused")
2626     @CalledByNative
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;
2634
2635         if (x1 != x2 || y1 != y2 ||
2636                 (mSelectionHandleController != null && mSelectionHandleController.isDragging())) {
2637             if (mInsertionHandleController != null) {
2638                 mInsertionHandleController.hide();
2639             }
2640             if (isAnchorFirst) {
2641                 mStartHandlePoint.setLocalDip(x1, y1);
2642                 mEndHandlePoint.setLocalDip(x2, y2);
2643             } else {
2644                 mStartHandlePoint.setLocalDip(x2, y2);
2645                 mEndHandlePoint.setLocalDip(x1, y1);
2646             }
2647
2648             boolean wereSelectionHandlesShowing = getSelectionHandleController().isShowing();
2649
2650             getSelectionHandleController().onSelectionChanged(anchorDir, focusDir);
2651             updateHandleScreenPositions();
2652             mHasSelection = true;
2653
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);
2658             }
2659
2660         } else {
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();
2667                 }
2668                 mInsertionHandlePoint.setLocalDip(x1, y1);
2669
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);
2678                 }
2679             } else {
2680                 // Deselection
2681                 if (mSelectionHandleController != null) {
2682                     mSelectionHandleController.hideAndDisallowAutomaticShowing();
2683                 }
2684                 if (mInsertionHandleController != null) {
2685                     mInsertionHandleController.hideAndDisallowAutomaticShowing();
2686                 }
2687             }
2688             mHasSelection = false;
2689         }
2690         if (isSelectionHandleShowing() || isInsertionHandleShowing()) {
2691             mPositionObserver.addListener(mPositionListener);
2692         }
2693     }
2694
2695     @SuppressWarnings("unused")
2696     @CalledByNative
2697     private static void onEvaluateJavaScriptResult(
2698             String jsonResult, JavaScriptCallback callback) {
2699         callback.handleJavaScriptResult(jsonResult);
2700     }
2701
2702     @SuppressWarnings("unused")
2703     @CalledByNative
2704     private void showPastePopup(int xDip, int yDip) {
2705         mInsertionHandlePoint.setLocalDip(xDip, yDip);
2706         getInsertionHandleController().showHandle();
2707         updateHandleScreenPositions();
2708         getInsertionHandleController().showHandleWithPastePopup();
2709     }
2710
2711     @SuppressWarnings("unused")
2712     @CalledByNative
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);
2719         }
2720
2721         attachImeAdapter();
2722     }
2723
2724     @SuppressWarnings("unused")
2725     @CalledByNative
2726     private void onWebContentsConnected() {
2727         attachImeAdapter();
2728     }
2729
2730     /**
2731      * Attaches the native ImeAdapter object to the java ImeAdapter to allow communication via JNI.
2732      */
2733     public void attachImeAdapter() {
2734         if (mImeAdapter != null && mNativeContentViewCore != 0) {
2735             mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2736         }
2737     }
2738
2739     /**
2740      * @see View#hasFocus()
2741      */
2742     @CalledByNative
2743     public boolean hasFocus() {
2744         return mContainerView.hasFocus();
2745     }
2746
2747     /**
2748      * Checks whether the ContentViewCore can be zoomed in.
2749      *
2750      * @return True if the ContentViewCore can be zoomed in.
2751      */
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;
2758     }
2759
2760     /**
2761      * Checks whether the ContentViewCore can be zoomed out.
2762      *
2763      * @return True if the ContentViewCore can be zoomed out.
2764      */
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;
2771     }
2772
2773     /**
2774      * Zooms in the ContentViewCore by 25% (or less if that would result in
2775      * zooming in more than possible).
2776      *
2777      * @return True if there was a zoom change, false otherwise.
2778      */
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() {
2782         if (!canZoomIn()) {
2783             return false;
2784         }
2785         return pinchByDelta(1.25f);
2786     }
2787
2788     /**
2789      * Zooms out the ContentViewCore by 20% (or less if that would result in
2790      * zooming out more than possible).
2791      *
2792      * @return True if there was a zoom change, false otherwise.
2793      */
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()) {
2798             return false;
2799         }
2800         return pinchByDelta(0.8f);
2801     }
2802
2803     /**
2804      * Resets the zoom factor of the ContentViewCore.
2805      *
2806      * @return True if there was a zoom change, false otherwise.
2807      */
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());
2817     }
2818
2819     /**
2820      * Simulate a pinch zoom gesture.
2821      *
2822      * @param delta the factor by which the current page scale should be multiplied by.
2823      * @return whether the gesture was sent.
2824      */
2825     public boolean pinchByDelta(float delta) {
2826         if (mNativeContentViewCore == 0) return false;
2827
2828         long timeMs = System.currentTimeMillis();
2829         int xPix = getViewportWidthPix() / 2;
2830         int yPix = getViewportHeightPix() / 2;
2831
2832         nativePinchBegin(mNativeContentViewCore, timeMs, xPix, yPix);
2833         nativePinchBy(mNativeContentViewCore, timeMs, xPix, yPix, delta);
2834         nativePinchEnd(mNativeContentViewCore, timeMs);
2835
2836         return true;
2837     }
2838
2839     /**
2840      * Invokes the graphical zoom picker widget for this ContentView.
2841      */
2842     public void invokeZoomPicker() {
2843         mZoomControlsDelegate.invokeZoomPicker();
2844     }
2845
2846     /**
2847      * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
2848      * and automatically pass in {@link JavascriptInterface} as the required annotation.
2849      *
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.
2853      */
2854     public void addJavascriptInterface(Object object, String name) {
2855         addPossiblyUnsafeJavascriptInterface(object, name, JavascriptInterface.class);
2856     }
2857
2858     /**
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>
2868      * <ul>
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>
2888      * </ul></p>
2889      *
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
2894      *                           JavaScript.
2895      * @param requiredAnnotation Restrict exposed methods to ones with this
2896      *                           annotation.  If {@code null} all methods are
2897      *                           exposed.
2898      *
2899      */
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);
2906         }
2907     }
2908
2909     /**
2910      * Removes a previously added JavaScript interface with the given name.
2911      *
2912      * @param name The name of the interface to remove.
2913      */
2914     public void removeJavascriptInterface(String name) {
2915         mJavaScriptInterfaces.remove(name);
2916         if (mNativeContentViewCore != 0) {
2917             nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2918         }
2919     }
2920
2921     /**
2922      * Return the current scale of the ContentView.
2923      * @return The current page scale factor.
2924      */
2925     public float getScale() {
2926         return mRenderCoordinates.getPageScaleFactor();
2927     }
2928
2929     /**
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.
2934      */
2935     public boolean isReady() {
2936         if (mNativeContentViewCore == 0) return false;
2937         return nativeIsRenderWidgetHostViewReady(mNativeContentViewCore);
2938     }
2939
2940     @CalledByNative
2941     private void startContentIntent(String contentUrl) {
2942         getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2943     }
2944
2945     @Override
2946     public void onAccessibilityStateChanged(boolean enabled) {
2947         setAccessibilityState(enabled);
2948     }
2949
2950     /**
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.
2954      */
2955     public boolean supportsAccessibilityAction(int action) {
2956         return mAccessibilityInjector.supportsAccessibilityAction(action);
2957     }
2958
2959     /**
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.
2968      */
2969     public boolean performAccessibilityAction(int action, Bundle arguments) {
2970         if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2971             return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2972         }
2973
2974         return false;
2975     }
2976
2977     /**
2978      * Set the BrowserAccessibilityManager, used for native accessibility
2979      * (not script injection). This is only set when system accessibility
2980      * has been enabled.
2981      * @param manager The new BrowserAccessibilityManager.
2982      */
2983     public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
2984         mBrowserAccessibilityManager = manager;
2985     }
2986
2987     /**
2988      * Get the BrowserAccessibilityManager, used for native accessibility
2989      * (not script injection). This will return null when system accessibility
2990      * is not enabled.
2991      * @return This view's BrowserAccessibilityManager.
2992      */
2993     public BrowserAccessibilityManager getBrowserAccessibilityManager() {
2994         return mBrowserAccessibilityManager;
2995     }
2996
2997     /**
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.
3003      */
3004     public AccessibilityNodeProvider getAccessibilityNodeProvider() {
3005         if (mBrowserAccessibilityManager != null) {
3006             return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
3007         }
3008
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);
3015         }
3016
3017         return null;
3018     }
3019
3020     /**
3021      * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
3022      */
3023     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
3024         // Note: this is only used by the script-injecting accessibility code.
3025         mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
3026     }
3027
3028     /**
3029      * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
3030      */
3031     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
3032         // Note: this is only used by the script-injecting accessibility code.
3033         event.setClassName(this.getClass().getName());
3034
3035         // Identify where the top-left of the screen currently points to.
3036         event.setScrollX(mRenderCoordinates.getScrollXPixInt());
3037         event.setScrollY(mRenderCoordinates.getScrollYPixInt());
3038
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);
3044
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);
3050         }
3051     }
3052
3053     /**
3054      * Returns whether accessibility script injection is enabled on the device
3055      */
3056     public boolean isDeviceAccessibilityScriptInjectionEnabled() {
3057         try {
3058             if (CommandLine.getInstance().hasSwitch(
3059                     ContentSwitches.DISABLE_ACCESSIBILITY_SCRIPT_INJECTION)) {
3060                 return false;
3061             }
3062
3063             if (!mContentSettings.getJavaScriptEnabled()) {
3064                 return false;
3065             }
3066
3067             int result = getContext().checkCallingOrSelfPermission(
3068                     android.Manifest.permission.INTERNET);
3069             if (result != PackageManager.PERMISSION_GRANTED) {
3070                 return false;
3071             }
3072
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();
3077
3078             if (mAccessibilityScriptInjectionObserver == null) {
3079                 ContentObserver contentObserver = new ContentObserver(new Handler()) {
3080                     @Override
3081                     public void onChange(boolean selfChange, Uri uri) {
3082                         setAccessibilityState(mAccessibilityManager.isEnabled());
3083                     }
3084                 };
3085                 contentResolver.registerContentObserver(
3086                     Settings.Secure.getUriFor(accessibilityScriptInjection),
3087                     false,
3088                     contentObserver);
3089                 mAccessibilityScriptInjectionObserver = contentObserver;
3090             }
3091
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.
3097         }
3098         return false;
3099     }
3100
3101     /**
3102      * Returns whether or not accessibility injection is being used.
3103      */
3104     public boolean isInjectingAccessibilityScript() {
3105         return mAccessibilityInjector.accessibilityIsAvailable();
3106     }
3107
3108     /**
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.
3113      */
3114     public void setAccessibilityState(boolean state) {
3115         if (!state) {
3116             setInjectedAccessibility(false);
3117             mNativeAccessibilityAllowed = false;
3118         } else {
3119             boolean useScriptInjection = isDeviceAccessibilityScriptInjectionEnabled();
3120             setInjectedAccessibility(useScriptInjection);
3121             mNativeAccessibilityAllowed = !useScriptInjection;
3122         }
3123     }
3124
3125     /**
3126      * Enable or disable injected accessibility features
3127      */
3128     public void setInjectedAccessibility(boolean enabled) {
3129         mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
3130         mAccessibilityInjector.setScriptEnabled(enabled);
3131     }
3132
3133     /**
3134      * Stop any TTS notifications that are currently going on.
3135      */
3136     public void stopCurrentAccessibilityNotifications() {
3137         mAccessibilityInjector.onPageLostFocus();
3138     }
3139
3140     /**
3141      * Inform WebKit that Fullscreen mode has been exited by the user.
3142      */
3143     public void exitFullscreen() {
3144         if (mNativeContentViewCore != 0) nativeExitFullscreen(mNativeContentViewCore);
3145     }
3146
3147     /**
3148      * Changes whether hiding the top controls is enabled.
3149      *
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.
3153      */
3154     public void updateTopControlsState(boolean enableHiding, boolean enableShowing,
3155             boolean animate) {
3156         if (mNativeContentViewCore != 0) {
3157             nativeUpdateTopControlsState(
3158                     mNativeContentViewCore, enableHiding, enableShowing, animate);
3159         }
3160     }
3161
3162     /**
3163      * Callback factory method for nativeGetNavigationHistory().
3164      */
3165     @CalledByNative
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);
3171     }
3172
3173     /**
3174      * Get a copy of the navigation history of the view.
3175      */
3176     public NavigationHistory getNavigationHistory() {
3177         NavigationHistory history = new NavigationHistory();
3178         if (mNativeContentViewCore != 0) {
3179             int currentIndex = nativeGetNavigationHistory(mNativeContentViewCore, history);
3180             history.setCurrentEntryIndex(currentIndex);
3181         }
3182         return history;
3183     }
3184
3185     @Override
3186     public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
3187         NavigationHistory history = new NavigationHistory();
3188         if (mNativeContentViewCore != 0) {
3189             nativeGetDirectedNavigationHistory(
3190                 mNativeContentViewCore, history, isForward, itemLimit);
3191         }
3192         return history;
3193     }
3194
3195     /**
3196      * @return The original request URL for the current navigation entry, or null if there is no
3197      *         current entry.
3198      */
3199     public String getOriginalUrlForActiveNavigationEntry() {
3200         if (mNativeContentViewCore != 0) {
3201             return nativeGetOriginalUrlForActiveNavigationEntry(mNativeContentViewCore);
3202         }
3203         return "";
3204     }
3205
3206     /**
3207      * @return The cached copy of render positions and scales.
3208      */
3209     public RenderCoordinates getRenderCoordinates() {
3210         return mRenderCoordinates;
3211     }
3212
3213     @CalledByNative
3214     private int getLocationInWindowX() {
3215         return mLocationInWindowX;
3216     }
3217
3218     @CalledByNative
3219     private int getLocationInWindowY() {
3220         return mLocationInWindowY;
3221     }
3222
3223     @CalledByNative
3224     private static Rect createRect(int x, int y, int right, int bottom) {
3225         return new Rect(x, y, right, bottom);
3226     }
3227
3228     public void attachExternalVideoSurface(int playerId, Surface surface) {
3229         if (mNativeContentViewCore != 0) {
3230             nativeAttachExternalVideoSurface(mNativeContentViewCore, playerId, surface);
3231         }
3232     }
3233
3234     public void detachExternalVideoSurface(int playerId) {
3235         if (mNativeContentViewCore != 0) {
3236             nativeDetachExternalVideoSurface(mNativeContentViewCore, playerId);
3237         }
3238     }
3239
3240     private boolean onAnimate(long frameTimeMicros) {
3241         if (mNativeContentViewCore == 0) return false;
3242         return nativeOnAnimate(mNativeContentViewCore, frameTimeMicros);
3243     }
3244
3245     private void animateIfNecessary(long frameTimeMicros) {
3246         if (mNeedAnimate) {
3247             mNeedAnimate = onAnimate(frameTimeMicros);
3248             if (!mNeedAnimate) removeVSyncSubscriber();
3249         }
3250     }
3251
3252     @CalledByNative
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));
3257     }
3258
3259     public void extractSmartClipData(int x, int y, int width, int height) {
3260         if (mNativeContentViewCore != 0) {
3261             nativeExtractSmartClipData(mNativeContentViewCore, x, y, width, height);
3262         }
3263     }
3264
3265     @CalledByNative
3266     private void onSmartClipDataExtracted(String result) {
3267         if (mSmartClipDataListener != null ) {
3268             mSmartClipDataListener.onSmartClipDataExtracted(result);
3269         }
3270     }
3271
3272     public void setSmartClipDataListener(SmartClipDataListener listener) {
3273         mSmartClipDataListener = listener;
3274     }
3275
3276     /**
3277      * Offer a long press gesture to the embedding View, primarily for WebView compatibility.
3278      *
3279      * @return true if the embedder handled the event.
3280      */
3281     private boolean offerLongPressToEmbedder() {
3282         return mContainerView.performLongClick();
3283     }
3284
3285     private native long nativeInit(long webContentsPtr,
3286             long viewAndroidPtr, long windowAndroidPtr);
3287
3288     @CalledByNative
3289     private ContentVideoViewClient getContentVideoViewClient() {
3290         return getContentViewClient().getContentVideoViewClient();
3291     }
3292
3293     @CalledByNative
3294     private boolean shouldBlockMediaRequest(String url) {
3295         return getContentViewClient().shouldBlockMediaRequest(url);
3296     }
3297
3298     @CalledByNative
3299     private void onNativeFlingStopped() {
3300         updateGestureStateListener(GestureEventType.FLING_END);
3301     }
3302
3303     private native WebContents nativeGetWebContentsAndroid(long nativeContentViewCoreImpl);
3304
3305     private native void nativeOnJavaContentViewCoreDestroyed(long nativeContentViewCoreImpl);
3306
3307     private native void nativeLoadUrl(
3308             long nativeContentViewCoreImpl,
3309             String url,
3310             int loadUrlType,
3311             int transitionType,
3312             int uaOverrideOption,
3313             String extraHeaders,
3314             byte[] postData,
3315             String baseUrlForDataUrl,
3316             String virtualUrlForDataUrl,
3317             boolean canLoadLocalResources);
3318
3319     private native String nativeGetURL(long nativeContentViewCoreImpl);
3320
3321     private native String nativeGetTitle(long nativeContentViewCoreImpl);
3322
3323     private native void nativeShowInterstitialPage(
3324             long nativeContentViewCoreImpl, String url, long nativeInterstitialPageDelegateAndroid);
3325     private native boolean nativeIsShowingInterstitialPage(long nativeContentViewCoreImpl);
3326
3327     private native boolean nativeIsIncognito(long nativeContentViewCoreImpl);
3328
3329     private native void nativeSetFocus(long nativeContentViewCoreImpl, boolean focused);
3330
3331     private native void nativeSendOrientationChangeEvent(
3332             long nativeContentViewCoreImpl, int orientation);
3333
3334     // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
3335     private native void nativeOnTouchEventHandlingBegin(
3336             long nativeContentViewCoreImpl, MotionEvent event);
3337
3338     private native void nativeOnTouchEventHandlingEnd(long nativeContentViewCoreImpl);
3339
3340     private native int nativeSendMouseMoveEvent(
3341             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3342
3343     private native int nativeSendMouseWheelEvent(
3344             long nativeContentViewCoreImpl, long timeMs, float x, float y, float verticalAxis);
3345
3346     private native void nativeScrollBegin(
3347             long nativeContentViewCoreImpl, long timeMs, float x, float y, float hintX,
3348             float hintY);
3349
3350     private native void nativeScrollEnd(long nativeContentViewCoreImpl, long timeMs);
3351
3352     private native void nativeScrollBy(
3353             long nativeContentViewCoreImpl, long timeMs, float x, float y,
3354             float deltaX, float deltaY);
3355
3356     private native void nativeFlingStart(
3357             long nativeContentViewCoreImpl, long timeMs, float x, float y, float vx, float vy);
3358
3359     private native void nativeFlingCancel(long nativeContentViewCoreImpl, long timeMs);
3360
3361     private native void nativeSingleTap(
3362             long nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
3363
3364     private native void nativeSingleTapUnconfirmed(
3365             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3366
3367     private native void nativeShowPress(
3368             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3369
3370     private native void nativeTapCancel(
3371             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3372
3373     private native void nativeTapDown(
3374             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3375
3376     private native void nativeDoubleTap(
3377             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3378
3379     private native void nativeLongPress(
3380             long nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
3381
3382     private native void nativeLongTap(
3383             long nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
3384
3385     private native void nativePinchBegin(
3386             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3387
3388     private native void nativePinchEnd(long nativeContentViewCoreImpl, long timeMs);
3389
3390     private native void nativePinchBy(long nativeContentViewCoreImpl, long timeMs,
3391             float anchorX, float anchorY, float deltaScale);
3392
3393     private native void nativeSelectBetweenCoordinates(
3394             long nativeContentViewCoreImpl, float x1, float y1, float x2, float y2);
3395
3396     private native void nativeMoveCaret(long nativeContentViewCoreImpl, float x, float y);
3397
3398     private native void nativeLoadIfNecessary(long nativeContentViewCoreImpl);
3399     private native void nativeRequestRestoreLoad(long nativeContentViewCoreImpl);
3400
3401     private native void nativeStopLoading(long nativeContentViewCoreImpl);
3402
3403     private native void nativeReload(long nativeContentViewCoreImpl, boolean checkForRepost);
3404     private native void nativeReloadIgnoringCache(
3405             long nativeContentViewCoreImpl, boolean checkForRepost);
3406
3407     private native void nativeCancelPendingReload(long nativeContentViewCoreImpl);
3408
3409     private native void nativeContinuePendingReload(long nativeContentViewCoreImpl);
3410
3411     private native void nativeSelectPopupMenuItems(long nativeContentViewCoreImpl, int[] indices);
3412
3413     private native void nativeScrollFocusedEditableNodeIntoView(long nativeContentViewCoreImpl);
3414     private native void nativeUndoScrollFocusedEditableNodeIntoView(long nativeContentViewCoreImpl);
3415
3416     private native void nativeClearHistory(long nativeContentViewCoreImpl);
3417
3418     private native void nativeEvaluateJavaScript(long nativeContentViewCoreImpl,
3419             String script, JavaScriptCallback callback, boolean startRenderer);
3420
3421     private native int nativeGetNativeImeAdapter(long nativeContentViewCoreImpl);
3422
3423     private native int nativeGetCurrentRenderProcessId(long nativeContentViewCoreImpl);
3424
3425     private native int nativeGetBackgroundColor(long nativeContentViewCoreImpl);
3426
3427     private native void nativeOnShow(long nativeContentViewCoreImpl);
3428     private native void nativeOnHide(long nativeContentViewCoreImpl);
3429
3430     private native void nativeSetUseDesktopUserAgent(long nativeContentViewCoreImpl,
3431             boolean enabled, boolean reloadOnChange);
3432     private native boolean nativeGetUseDesktopUserAgent(long nativeContentViewCoreImpl);
3433
3434     private native void nativeClearSslPreferences(long nativeContentViewCoreImpl);
3435
3436     private native void nativeAddJavascriptInterface(long nativeContentViewCoreImpl, Object object,
3437             String name, Class requiredAnnotation, HashSet<Object> retainedObjectSet);
3438
3439     private native void nativeRemoveJavascriptInterface(long nativeContentViewCoreImpl,
3440             String name);
3441
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);
3447
3448     private native void nativeUpdateVSyncParameters(long nativeContentViewCoreImpl,
3449             long timebaseMicros, long intervalMicros);
3450
3451     private native void nativeOnVSync(long nativeContentViewCoreImpl, long frameTimeMicros);
3452
3453     private native boolean nativeOnAnimate(long nativeContentViewCoreImpl, long frameTimeMicros);
3454
3455     private native boolean nativePopulateBitmapFromCompositor(long nativeContentViewCoreImpl,
3456             Bitmap bitmap);
3457
3458     private native void nativeWasResized(long nativeContentViewCoreImpl);
3459
3460     private native boolean nativeIsRenderWidgetHostViewReady(long nativeContentViewCoreImpl);
3461
3462     private native void nativeExitFullscreen(long nativeContentViewCoreImpl);
3463     private native void nativeUpdateTopControlsState(long nativeContentViewCoreImpl,
3464             boolean enableHiding, boolean enableShowing, boolean animate);
3465
3466     private native void nativeShowImeIfNeeded(long nativeContentViewCoreImpl);
3467
3468     private native void nativeAttachExternalVideoSurface(
3469             long nativeContentViewCoreImpl, int playerId, Surface surface);
3470
3471     private native void nativeDetachExternalVideoSurface(
3472             long nativeContentViewCoreImpl, int playerId);
3473
3474     private native void nativeSetAccessibilityEnabled(
3475             long nativeContentViewCoreImpl, boolean enabled);
3476
3477     private native void nativeSendSingleTapUma(long nativeContentViewCoreImpl,
3478             int type, int count);
3479
3480     private native void nativeSendActionAfterDoubleTapUma(long nativeContentViewCoreImpl,
3481             int type, boolean hasDelay, int count);
3482
3483     private native void nativeExtractSmartClipData(long nativeContentViewCoreImpl,
3484             int x, int y, int w, int h);
3485 }