Update To 11.40.268.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.annotation.SuppressLint;
8 import android.app.Activity;
9 import android.app.SearchManager;
10 import android.content.ClipboardManager;
11 import android.content.ContentResolver;
12 import android.content.Context;
13 import android.content.Intent;
14 import android.content.pm.PackageManager;
15 import android.content.res.Configuration;
16 import android.database.ContentObserver;
17 import android.graphics.Bitmap;
18 import android.graphics.Canvas;
19 import android.graphics.Rect;
20 import android.net.Uri;
21 import android.os.Build;
22 import android.os.Bundle;
23 import android.os.Handler;
24 import android.os.ResultReceiver;
25 import android.os.SystemClock;
26 import android.provider.Browser;
27 import android.provider.Settings;
28 import android.text.Editable;
29 import android.text.Selection;
30 import android.text.TextUtils;
31 import android.util.Log;
32 import android.util.Pair;
33 import android.view.ActionMode;
34 import android.view.HapticFeedbackConstants;
35 import android.view.InputDevice;
36 import android.view.KeyEvent;
37 import android.view.MotionEvent;
38 import android.view.View;
39 import android.view.ViewGroup;
40 import android.view.accessibility.AccessibilityEvent;
41 import android.view.accessibility.AccessibilityManager;
42 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
43 import android.view.accessibility.AccessibilityNodeInfo;
44 import android.view.accessibility.AccessibilityNodeProvider;
45 import android.view.inputmethod.EditorInfo;
46 import android.view.inputmethod.InputConnection;
47 import android.view.inputmethod.InputMethodManager;
48 import android.widget.FrameLayout;
49
50 import org.chromium.base.ApiCompatibilityUtils;
51 import org.chromium.base.CalledByNative;
52 import org.chromium.base.CommandLine;
53 import org.chromium.base.JNINamespace;
54 import org.chromium.base.ObserverList;
55 import org.chromium.base.ObserverList.RewindableIterator;
56 import org.chromium.base.TraceEvent;
57 import org.chromium.base.VisibleForTesting;
58 import org.chromium.content.R;
59 import org.chromium.content.browser.ScreenOrientationListener.ScreenOrientationObserver;
60 import org.chromium.content.browser.accessibility.AccessibilityInjector;
61 import org.chromium.content.browser.accessibility.BrowserAccessibilityManager;
62 import org.chromium.content.browser.input.AdapterInputConnection;
63 import org.chromium.content.browser.input.GamepadList;
64 import org.chromium.content.browser.input.ImeAdapter;
65 import org.chromium.content.browser.input.ImeAdapter.AdapterInputConnectionFactory;
66 import org.chromium.content.browser.input.InputMethodManagerWrapper;
67 import org.chromium.content.browser.input.PastePopupMenu;
68 import org.chromium.content.browser.input.PastePopupMenu.PastePopupMenuDelegate;
69 import org.chromium.content.browser.input.PopupTouchHandleDrawable;
70 import org.chromium.content.browser.input.PopupTouchHandleDrawable.PopupTouchHandleDrawableDelegate;
71 import org.chromium.content.browser.input.SelectPopup;
72 import org.chromium.content.browser.input.SelectPopupDialog;
73 import org.chromium.content.browser.input.SelectPopupDropdown;
74 import org.chromium.content.browser.input.SelectPopupItem;
75 import org.chromium.content.browser.input.SelectionEventType;
76 import org.chromium.content.common.ContentSwitches;
77 import org.chromium.content_public.browser.GestureStateListener;
78 import org.chromium.content_public.browser.WebContents;
79 import org.chromium.ui.base.DeviceFormFactor;
80 import org.chromium.ui.base.ViewAndroid;
81 import org.chromium.ui.base.ViewAndroidDelegate;
82 import org.chromium.ui.base.WindowAndroid;
83 import org.chromium.ui.gfx.DeviceDisplayInfo;
84
85 import java.lang.annotation.Annotation;
86 import java.lang.reflect.Field;
87 import java.util.ArrayList;
88 import java.util.HashMap;
89 import java.util.HashSet;
90 import java.util.LinkedHashMap;
91 import java.util.List;
92 import java.util.Map;
93 import java.util.Map.Entry;
94
95 /**
96  * Provides a Java-side 'wrapper' around a WebContent (native) instance.
97  * Contains all the major functionality necessary to manage the lifecycle of a ContentView without
98  * being tied to the view system.
99  */
100 @JNINamespace("content")
101 public class ContentViewCore
102         implements AccessibilityStateChangeListener, ScreenOrientationObserver {
103
104     private static final String TAG = "ContentViewCore";
105
106     // Used to avoid enabling zooming in / out if resulting zooming will
107     // produce little visible difference.
108     private static final float ZOOM_CONTROLS_EPSILON = 0.007f;
109
110     // Used to represent gestures for long press and long tap.
111     private static final int IS_LONG_PRESS = 1;
112     private static final int IS_LONG_TAP = 2;
113
114     private static final ZoomControlsDelegate NO_OP_ZOOM_CONTROLS_DELEGATE =
115             new ZoomControlsDelegate() {
116         @Override
117         public void invokeZoomPicker() {}
118         @Override
119         public void dismissZoomPicker() {}
120         @Override
121         public void updateZoomControls() {}
122     };
123
124     // If the embedder adds a JavaScript interface object that contains an indirect reference to
125     // the ContentViewCore, then storing a strong ref to the interface object on the native
126     // side would prevent garbage collection of the ContentViewCore (as that strong ref would
127     // create a new GC root).
128     // For that reason, we store only a weak reference to the interface object on the
129     // native side. However we still need a strong reference on the Java side to
130     // prevent garbage collection if the embedder doesn't maintain their own ref to the
131     // interface object - the Java side ref won't create a new GC root.
132     // This map stores those references. We put into the map on addJavaScriptInterface()
133     // and remove from it in removeJavaScriptInterface(). The annotation class is stored for
134     // the purpose of migrating injected objects from one instance of CVC to another, which
135     // is used by Android WebView to support WebChromeClient.onCreateWindow scenario.
136     private final Map<String, Pair<Object, Class>> mJavaScriptInterfaces =
137             new HashMap<String, Pair<Object, Class>>();
138
139     // Additionally, we keep track of all Java bound JS objects that are in use on the
140     // current page to ensure that they are not garbage collected until the page is
141     // navigated. This includes interface objects that have been removed
142     // via the removeJavaScriptInterface API and transient objects returned from methods
143     // on the interface object. Note we use HashSet rather than Set as the native side
144     // expects HashSet (no bindings for interfaces).
145     private final HashSet<Object> mRetainedJavaScriptObjects = new HashSet<Object>();
146
147     /**
148      * A {@link ViewAndroidDelegate} that delegates to the current container view.
149      *
150      * <p>This delegate handles the replacement of container views transparently so
151      * that clients can safely hold to instances of this class.
152      */
153     private class ContentViewAndroidDelegate implements ViewAndroidDelegate {
154         /**
155          * Represents the position of an anchor view.
156          */
157         @VisibleForTesting
158         private class Position {
159             private final float mX;
160             private final float mY;
161             private final float mWidth;
162             private final float mHeight;
163
164             public Position(float x, float y, float width, float height) {
165                 mX = x;
166                 mY = y;
167                 mWidth = width;
168                 mHeight = height;
169             }
170         }
171
172         /**
173          * The current container view. This view can be updated with
174          * {@link #updateCurrentContainerView()}.
175          */
176         private ViewGroup mCurrentContainerView;
177
178         /**
179          * List of anchor views stored in the order in which they were acquired mapped
180          * to their position.
181          */
182         private Map<View, Position> mAnchorViews = new LinkedHashMap<View, Position>();
183
184         @Override
185         public View acquireAnchorView() {
186             View anchorView = new View(mContext);
187             mAnchorViews.put(anchorView, null);
188             mCurrentContainerView.addView(anchorView);
189             return anchorView;
190         }
191
192         @Override
193         public void setAnchorViewPosition(
194                 View view, float x, float y, float width, float height) {
195             mAnchorViews.put(view, new Position(x, y, width, height));
196             doSetAnchorViewPosition(view, x, y, width, height);
197         }
198
199         @SuppressWarnings("deprecation")  // AbsoluteLayout
200         private void doSetAnchorViewPosition(
201                 View view, float x, float y, float width, float height) {
202             if (view.getParent() == null) {
203                 // Ignore. setAnchorViewPosition has been called after the anchor view has
204                 // already been released.
205                 return;
206             }
207             assert view.getParent() == mCurrentContainerView;
208
209             float scale = (float) DeviceDisplayInfo.create(mContext).getDIPScale();
210
211             // The anchor view should not go outside the bounds of the ContainerView.
212             int leftMargin = Math.round(x * scale);
213             int topMargin = Math.round(mRenderCoordinates.getContentOffsetYPix() + y * scale);
214             int scaledWidth = Math.round(width * scale);
215             // ContentViewCore currently only supports these two container view types.
216             if (mCurrentContainerView instanceof FrameLayout) {
217                 int startMargin;
218                 if (ApiCompatibilityUtils.isLayoutRtl(mCurrentContainerView)) {
219                     startMargin = mCurrentContainerView.getMeasuredWidth()
220                             - Math.round((width + x) * scale);
221                 } else {
222                     startMargin = leftMargin;
223                 }
224                 if (scaledWidth + startMargin > mCurrentContainerView.getWidth()) {
225                     scaledWidth = mCurrentContainerView.getWidth() - startMargin;
226                 }
227                 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
228                         scaledWidth, Math.round(height * scale));
229                 ApiCompatibilityUtils.setMarginStart(lp, startMargin);
230                 lp.topMargin = topMargin;
231                 view.setLayoutParams(lp);
232             } else if (mCurrentContainerView instanceof android.widget.AbsoluteLayout) {
233                 // This fixes the offset due to a difference in
234                 // scrolling model of WebView vs. Chrome.
235                 // TODO(sgurun) fix this to use mContainerViewAtCreation.getScroll[X/Y]()
236                 // as it naturally accounts for scroll differences between
237                 // these models.
238                 leftMargin += mRenderCoordinates.getScrollXPixInt();
239                 topMargin += mRenderCoordinates.getScrollYPixInt();
240
241                 android.widget.AbsoluteLayout.LayoutParams lp =
242                         new android.widget.AbsoluteLayout.LayoutParams(
243                             scaledWidth, (int) (height * scale), leftMargin, topMargin);
244                 view.setLayoutParams(lp);
245             } else {
246                 Log.e(TAG, "Unknown layout " + mCurrentContainerView.getClass().getName());
247             }
248         }
249
250         @Override
251         public void releaseAnchorView(View anchorView) {
252             mAnchorViews.remove(anchorView);
253             mCurrentContainerView.removeView(anchorView);
254         }
255
256         /**
257          * Updates (or sets for the first time) the current container view to which
258          * this class delegates. Existing anchor views are transferred from the old to
259          * the new container view.
260          */
261         void updateCurrentContainerView() {
262             ViewGroup oldContainerView = mCurrentContainerView;
263             mCurrentContainerView = mContainerView;
264             for (Entry<View, Position> entry : mAnchorViews.entrySet()) {
265                 View anchorView = entry.getKey();
266                 Position position = entry.getValue();
267                 oldContainerView.removeView(anchorView);
268                 mCurrentContainerView.addView(anchorView);
269                 if (position != null) {
270                     doSetAnchorViewPosition(anchorView,
271                             position.mX, position.mY, position.mWidth, position.mHeight);
272                 }
273             }
274         }
275     }
276
277     /**
278      * Interface that consumers of {@link ContentViewCore} must implement to allow the proper
279      * dispatching of view methods through the containing view.
280      *
281      * <p>
282      * All methods with the "super_" prefix should be routed to the parent of the
283      * implementing container view.
284      */
285     @SuppressWarnings("javadoc")
286     public interface InternalAccessDelegate {
287         /**
288          * @see View#drawChild(Canvas, View, long)
289          */
290         boolean drawChild(Canvas canvas, View child, long drawingTime);
291
292         /**
293          * @see View#onKeyUp(keyCode, KeyEvent)
294          */
295         boolean super_onKeyUp(int keyCode, KeyEvent event);
296
297         /**
298          * @see View#dispatchKeyEventPreIme(KeyEvent)
299          */
300         boolean super_dispatchKeyEventPreIme(KeyEvent event);
301
302         /**
303          * @see View#dispatchKeyEvent(KeyEvent)
304          */
305         boolean super_dispatchKeyEvent(KeyEvent event);
306
307         /**
308          * @see View#onGenericMotionEvent(MotionEvent)
309          */
310         boolean super_onGenericMotionEvent(MotionEvent event);
311
312         /**
313          * @see View#onConfigurationChanged(Configuration)
314          */
315         void super_onConfigurationChanged(Configuration newConfig);
316
317         /**
318          * @see View#onScrollChanged(int, int, int, int)
319          */
320         void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix);
321
322         /**
323          * @see View#awakenScrollBars()
324          */
325         boolean awakenScrollBars();
326
327         /**
328          * @see View#awakenScrollBars(int, boolean)
329          */
330         boolean super_awakenScrollBars(int startDelay, boolean invalidate);
331     }
332
333     /**
334      * An interface for controlling visibility and state of embedder-provided zoom controls.
335      */
336     public interface ZoomControlsDelegate {
337         /**
338          * Called when it's reasonable to show zoom controls.
339          */
340         void invokeZoomPicker();
341
342         /**
343          * Called when zoom controls need to be hidden (e.g. when the view hides).
344          */
345         void dismissZoomPicker();
346
347         /**
348          * Called when page scale has been changed, so the controls can update their state.
349          */
350         void updateZoomControls();
351     }
352
353     /**
354      * An interface that allows the embedder to be notified when the results of
355      * extractSmartClipData are available.
356      */
357     public interface SmartClipDataListener {
358         public void onSmartClipDataExtracted(String text, String html, Rect clipRect);
359     }
360
361     private final Context mContext;
362     private ViewGroup mContainerView;
363     private InternalAccessDelegate mContainerViewInternals;
364     private WebContents mWebContents;
365     private WebContentsObserver mWebContentsObserver;
366
367     private ContentViewClient mContentViewClient;
368
369     private ContentSettings mContentSettings;
370
371     // Native pointer to C++ ContentViewCoreImpl object which will be set by nativeInit().
372     private long mNativeContentViewCore = 0;
373
374     private final ObserverList<GestureStateListener> mGestureStateListeners;
375     private final RewindableIterator<GestureStateListener> mGestureStateListenersIterator;
376     private ZoomControlsDelegate mZoomControlsDelegate;
377
378     private PopupZoomer mPopupZoomer;
379     private SelectPopup mSelectPopup;
380     private long mNativeSelectPopupSourceFrame = 0;
381
382     private Runnable mFakeMouseMoveRunnable = null;
383
384     // Only valid when focused on a text / password field.
385     private ImeAdapter mImeAdapter;
386     private ImeAdapter.AdapterInputConnectionFactory mAdapterInputConnectionFactory;
387     private AdapterInputConnection mInputConnection;
388     private InputMethodManagerWrapper mInputMethodManagerWrapper;
389
390     // Lazily created paste popup menu, triggered either via long press in an
391     // editable region or from tapping the insertion handle.
392     private PastePopupMenu mPastePopupMenu;
393     private boolean mWasPastePopupShowingOnInsertionDragStart;
394
395     private PopupTouchHandleDrawableDelegate mTouchHandleDelegate;
396
397     private PositionObserver mPositionObserver;
398
399     // Size of the viewport in physical pixels as set from onSizeChanged.
400     private int mViewportWidthPix;
401     private int mViewportHeightPix;
402     private int mPhysicalBackingWidthPix;
403     private int mPhysicalBackingHeightPix;
404     private int mTopControlsLayoutHeightPix;
405
406     // Cached copy of all positions and scales as reported by the renderer.
407     private final RenderCoordinates mRenderCoordinates;
408
409     // Tracks whether a selection is currently active.  When applied to selected text, indicates
410     // whether the last selected text is still highlighted.
411     private boolean mHasSelection;
412     private boolean mHasInsertion;
413     private String mLastSelectedText;
414     private boolean mFocusedNodeEditable;
415     private ActionMode mActionMode;
416     private boolean mUnselectAllOnActionModeDismiss;
417     private boolean mPreserveSelectionOnNextLossOfFocus;
418     private SelectActionModeCallback.ActionHandler mActionHandler;
419
420     // Delegate that will handle GET downloads, and be notified of completion of POST downloads.
421     private ContentViewDownloadDelegate mDownloadDelegate;
422
423     // The AccessibilityInjector that handles loading Accessibility scripts into the web page.
424     private AccessibilityInjector mAccessibilityInjector;
425
426     // Whether native accessibility, i.e. without any script injection, is allowed.
427     private boolean mNativeAccessibilityAllowed;
428
429     // Whether native accessibility, i.e. without any script injection, has been enabled.
430     private boolean mNativeAccessibilityEnabled;
431
432     // Handles native accessibility, i.e. without any script injection.
433     private BrowserAccessibilityManager mBrowserAccessibilityManager;
434
435     // System accessibility service.
436     private final AccessibilityManager mAccessibilityManager;
437
438     // Accessibility touch exploration state.
439     private boolean mTouchExplorationEnabled;
440
441     // Whether accessibility focus should be set to the page when it finishes loading.
442     // This only applies if an accessibility service like TalkBack is running.
443     // This is desirable behavior for a browser window, but not for an embedded
444     // WebView.
445     private boolean mShouldSetAccessibilityFocusOnPageLoad;
446
447     // Allows us to dynamically respond when the accessibility script injection flag changes.
448     private ContentObserver mAccessibilityScriptInjectionObserver;
449
450     // Temporary notification to tell onSizeChanged to focus a form element,
451     // because the OSK was just brought up.
452     private final Rect mFocusPreOSKViewportRect = new Rect();
453
454     // On tap this will store the x, y coordinates of the touch.
455     private int mLastTapX;
456     private int mLastTapY;
457
458     // Whether a touch scroll sequence is active, used to hide text selection
459     // handles. Note that a scroll sequence will *always* bound a pinch
460     // sequence, so this will also be true for the duration of a pinch gesture.
461     private boolean mTouchScrollInProgress;
462
463     // The outstanding fling start events that hasn't got fling end yet. It may be > 1 because
464     // onNativeFlingStopped() is called asynchronously.
465     private int mPotentiallyActiveFlingCount;
466
467     private ViewAndroid mViewAndroid;
468
469     private SmartClipDataListener mSmartClipDataListener = null;
470
471     // This holds the state of editable text (e.g. contents of <input>, contenteditable) of
472     // a focused element.
473     // Every time the user, IME, javascript (Blink), autofill etc. modifies the content, the new
474     //  state must be reflected to this to keep consistency.
475     private final Editable mEditable;
476
477     /**
478      * PID used to indicate an invalid render process.
479      */
480     // Keep in sync with the value returned from ContentViewCoreImpl::GetCurrentRendererProcessId()
481     // if there is no render process.
482     public static final int INVALID_RENDER_PROCESS_PID = 0;
483
484     // Offsets for the events that passes through this ContentViewCore.
485     private float mCurrentTouchOffsetX;
486     private float mCurrentTouchOffsetY;
487
488     // Offsets for smart clip
489     private int mSmartClipOffsetX;
490     private int mSmartClipOffsetY;
491
492     // Whether the ContentViewCore requires the WebContents to be fullscreen in order to lock the
493     // screen orientation.
494     private boolean mFullscreenRequiredForOrientationLock = true;
495
496     // A ViewAndroidDelegate that delegates to the current container view.
497     private ContentViewAndroidDelegate mViewAndroidDelegate;
498
499     /**
500      * Constructs a new ContentViewCore. Embedders must call initialize() after constructing
501      * a ContentViewCore and before using it.
502      *
503      * @param context The context used to create this.
504      */
505     public ContentViewCore(Context context) {
506         mContext = context;
507
508         mAdapterInputConnectionFactory = new AdapterInputConnectionFactory();
509         mInputMethodManagerWrapper = new InputMethodManagerWrapper(mContext);
510
511         mRenderCoordinates = new RenderCoordinates();
512         float deviceScaleFactor = getContext().getResources().getDisplayMetrics().density;
513         String forceScaleFactor = CommandLine.getInstance().getSwitchValue(
514                 ContentSwitches.FORCE_DEVICE_SCALE_FACTOR);
515         if (forceScaleFactor != null) {
516             deviceScaleFactor = Float.valueOf(forceScaleFactor);
517         }
518         mRenderCoordinates.setDeviceScaleFactor(deviceScaleFactor);
519         mAccessibilityManager = (AccessibilityManager)
520                 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
521         mGestureStateListeners = new ObserverList<GestureStateListener>();
522         mGestureStateListenersIterator = mGestureStateListeners.rewindableIterator();
523
524         mEditable = Editable.Factory.getInstance().newEditable("");
525         Selection.setSelection(mEditable, 0);
526     }
527
528     /**
529      * @return The context used for creating this ContentViewCore.
530      */
531     @CalledByNative
532     public Context getContext() {
533         return mContext;
534     }
535
536     /**
537      * @return The ViewGroup that all view actions of this ContentViewCore should interact with.
538      */
539     public ViewGroup getContainerView() {
540         return mContainerView;
541     }
542
543     /**
544      * @return The WebContents currently being rendered.
545      */
546     public WebContents getWebContents() {
547         return mWebContents;
548     }
549
550     /* TODO(aelias): Remove this after downstream callers switch to setTopControlsLayoutHeight. */
551     public void setViewportSizeOffset(int offsetXPix, int offsetYPix) {
552         setTopControlsLayoutHeight(offsetYPix);
553     }
554
555     /**
556      * Specifies how much smaller the Blink layout size should be relative to the size of this
557      * view.
558      * @param topControlsLayoutHeightPix The Y amount in pixels to shrink the viewport by.
559      */
560     public void setTopControlsLayoutHeight(int topControlsLayoutHeightPix) {
561         if (topControlsLayoutHeightPix != mTopControlsLayoutHeightPix) {
562             mTopControlsLayoutHeightPix = topControlsLayoutHeightPix;
563             if (mNativeContentViewCore != 0) nativeWasResized(mNativeContentViewCore);
564         }
565     }
566
567     /**
568      * Returns a delegate that can be used to add and remove views from the current
569      * container view. Clients can safely hold to instances of this class as it handles the
570      * replacement of container views transparently.
571      *
572      * NOTE: Use with care, as not all ContentViewCore users setup their container view in the same
573      * way. In particular, the Android WebView has limitations on what implementation details can
574      * be provided via a child view, as they are visible in the API and could introduce
575      * compatibility breaks with existing applications. If in doubt, contact the
576      * android_webview/OWNERS
577      *
578      * @return A ViewAndroidDelegate that can be used to add and remove views.
579      */
580     public ViewAndroidDelegate getViewAndroidDelegate() {
581         return mViewAndroidDelegate;
582     }
583
584     @VisibleForTesting
585     public void setImeAdapterForTest(ImeAdapter imeAdapter) {
586         mImeAdapter = imeAdapter;
587     }
588
589     @VisibleForTesting
590     public ImeAdapter getImeAdapterForTest() {
591         return mImeAdapter;
592     }
593
594     @VisibleForTesting
595     public void setAdapterInputConnectionFactory(AdapterInputConnectionFactory factory) {
596         mAdapterInputConnectionFactory = factory;
597     }
598
599     @VisibleForTesting
600     public void setInputMethodManagerWrapperForTest(InputMethodManagerWrapper immw) {
601         mInputMethodManagerWrapper = immw;
602     }
603
604     @VisibleForTesting
605     public AdapterInputConnection getInputConnectionForTest() {
606         return mInputConnection;
607     }
608
609     @VisibleForTesting
610     ViewAndroid getViewAndroid() {
611         return mViewAndroid;
612     }
613
614     private ImeAdapter createImeAdapter(Context context) {
615         return new ImeAdapter(mInputMethodManagerWrapper,
616                 new ImeAdapter.ImeAdapterDelegate() {
617                     @Override
618                     public void onImeEvent() {
619                         mPopupZoomer.hide(true);
620                         getContentViewClient().onImeEvent();
621                         if (mFocusedNodeEditable) dismissTextHandles();
622                     }
623
624                     @Override
625                     public void onDismissInput() {
626                         getContentViewClient().onImeStateChangeRequested(false);
627                     }
628
629                     @Override
630                     public View getAttachedView() {
631                         return mContainerView;
632                     }
633
634                     @Override
635                     public ResultReceiver getNewShowKeyboardReceiver() {
636                         return new ResultReceiver(new Handler()) {
637                             @Override
638                             public void onReceiveResult(int resultCode, Bundle resultData) {
639                                 getContentViewClient().onImeStateChangeRequested(
640                                         resultCode == InputMethodManager.RESULT_SHOWN
641                                         || resultCode == InputMethodManager.RESULT_UNCHANGED_SHOWN);
642                                 if (resultCode == InputMethodManager.RESULT_SHOWN) {
643                                     // If OSK is newly shown, delay the form focus until
644                                     // the onSizeChanged (in order to adjust relative to the
645                                     // new size).
646                                     // TODO(jdduke): We should not assume that onSizeChanged will
647                                     // always be called, crbug.com/294908.
648                                     getContainerView().getWindowVisibleDisplayFrame(
649                                             mFocusPreOSKViewportRect);
650                                 } else if (hasFocus() && resultCode
651                                         == InputMethodManager.RESULT_UNCHANGED_SHOWN) {
652                                     // If the OSK was already there, focus the form immediately.
653                                     assert mWebContents != null;
654                                     mWebContents.scrollFocusedEditableNodeIntoView();
655                                 }
656                             }
657                         };
658                     }
659                 }
660         );
661     }
662
663     /**
664      *
665      * @param containerView The view that will act as a container for all views created by this.
666      * @param internalDispatcher Handles dispatching all hidden or super methods to the
667      *                           containerView.
668      * @param nativeWebContents A pointer to the native web contents.
669      * @param windowAndroid An instance of the WindowAndroid.
670      */
671     // Perform important post-construction set up of the ContentViewCore.
672     // We do not require the containing view in the constructor to allow embedders to create a
673     // ContentViewCore without having fully created its containing view. The containing view
674     // is a vital component of the ContentViewCore, so embedders must exercise caution in what
675     // they do with the ContentViewCore before calling initialize().
676     // We supply the nativeWebContents pointer here rather than in the constructor to allow us
677     // to set the private browsing mode at a later point for the WebView implementation.
678     // Note that the caller remains the owner of the nativeWebContents and is responsible for
679     // deleting it after destroying the ContentViewCore.
680     public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
681             long nativeWebContents, WindowAndroid windowAndroid) {
682         createContentViewAndroidDelegate();
683         setContainerView(containerView);
684         long windowNativePointer = windowAndroid.getNativePointer();
685         assert windowNativePointer != 0;
686         createViewAndroid(windowAndroid);
687
688         long viewAndroidNativePointer = mViewAndroid.getNativePointer();
689         assert viewAndroidNativePointer != 0;
690
691         mZoomControlsDelegate = NO_OP_ZOOM_CONTROLS_DELEGATE;
692
693         mNativeContentViewCore = nativeInit(
694                 nativeWebContents, viewAndroidNativePointer, windowNativePointer,
695                 mRetainedJavaScriptObjects);
696         mWebContents = nativeGetWebContentsAndroid(mNativeContentViewCore);
697         mContentSettings = new ContentSettings(this, mNativeContentViewCore);
698
699         setContainerViewInternals(internalDispatcher);
700         mRenderCoordinates.reset();
701         initPopupZoomer(mContext);
702         mImeAdapter = createImeAdapter(mContext);
703         attachImeAdapter();
704
705         mAccessibilityInjector = AccessibilityInjector.newInstance(this);
706
707         mWebContentsObserver = new WebContentsObserver(mWebContents) {
708             @Override
709             public void didFailLoad(boolean isProvisionalLoad, boolean isMainFrame, int errorCode,
710                     String description, String failingUrl) {
711                 // Navigation that fails the provisional load will have the strong binding removed
712                 // here. One for which the provisional load is commited will have the strong binding
713                 // removed in navigationEntryCommitted() below.
714                 if (isProvisionalLoad) determinedProcessVisibility();
715             }
716
717             @Override
718             public void didNavigateMainFrame(String url, String baseUrl,
719                     boolean isNavigationToDifferentPage, boolean isFragmentNavigation) {
720                 if (!isNavigationToDifferentPage) return;
721                 hidePopupsAndClearSelection();
722                 resetScrollInProgress();
723                 resetGestureDetection();
724             }
725
726             @Override
727             public void renderProcessGone(boolean wasOomProtected) {
728                 hidePopupsAndClearSelection();
729                 resetScrollInProgress();
730                 // No need to reset gesture detection as the detector will have
731                 // been destroyed in the RenderWidgetHostView.
732             }
733
734             @Override
735             public void navigationEntryCommitted() {
736                 determinedProcessVisibility();
737             }
738
739             private void determinedProcessVisibility() {
740                 // Signal to the process management logic that we can now rely on the process
741                 // visibility signal for binding management. Before the navigation commits, its
742                 // renderer is considered background even if the pending navigation happens in the
743                 // foreground renderer.
744                 ChildProcessLauncher.determinedVisibility(getCurrentRenderProcessId());
745             }
746         };
747     }
748
749     @VisibleForTesting
750     void createContentViewAndroidDelegate() {
751         mViewAndroidDelegate = new ContentViewAndroidDelegate();
752     }
753
754     @VisibleForTesting
755     void createViewAndroid(WindowAndroid windowAndroid) {
756         mViewAndroid = new ViewAndroid(windowAndroid, mViewAndroidDelegate);
757     }
758
759     /**
760      * Sets a new container view for this {@link ContentViewCore}.
761      *
762      * <p>WARNING: This method can also be used to replace the existing container view,
763      * but you should only do it if you have a very good reason to. Replacing the
764      * container view has been designed to support fullscreen in the Webview so it
765      * might not be appropriate for other use cases.
766      *
767      * <p>This method only performs a small part of replacing the container view and
768      * embedders are responsible for:
769      * <ul>
770      *     <li>Disconnecting the old container view from this ContentViewCore</li>
771      *     <li>Updating the InternalAccessDelegate</li>
772      *     <li>Reconciling the state of this ContentViewCore with the new container view</li>
773      *     <li>Tearing down and recreating the native GL rendering where appropriate</li>
774      *     <li>etc.</li>
775      * </ul>
776      */
777     public void setContainerView(ViewGroup containerView) {
778         TraceEvent.begin();
779         if (mContainerView != null) {
780             mPastePopupMenu = null;
781             mInputConnection = null;
782             hidePopupsAndClearSelection();
783         }
784
785         mContainerView = containerView;
786         mPositionObserver = new ViewPositionObserver(mContainerView);
787         mContainerView.setClickable(true);
788         mViewAndroidDelegate.updateCurrentContainerView();
789         TraceEvent.end();
790     }
791
792     @CalledByNative
793     void onNativeContentViewCoreDestroyed(long nativeContentViewCore) {
794         assert nativeContentViewCore == mNativeContentViewCore;
795         mNativeContentViewCore = 0;
796     }
797
798     /**
799      * Set the Container view Internals.
800      * @param internalDispatcher Handles dispatching all hidden or super methods to the
801      *                           containerView.
802      */
803     public void setContainerViewInternals(InternalAccessDelegate internalDispatcher) {
804         mContainerViewInternals = internalDispatcher;
805     }
806
807     @VisibleForTesting
808     void initPopupZoomer(Context context) {
809         mPopupZoomer = new PopupZoomer(context);
810         mPopupZoomer.setOnVisibilityChangedListener(new PopupZoomer.OnVisibilityChangedListener() {
811             // mContainerView can change, but this OnVisibilityChangedListener can only be used
812             // to add and remove views from the mContainerViewAtCreation.
813             private final ViewGroup mContainerViewAtCreation = mContainerView;
814
815             @Override
816             public void onPopupZoomerShown(final PopupZoomer zoomer) {
817                 mContainerViewAtCreation.post(new Runnable() {
818                     @Override
819                     public void run() {
820                         if (mContainerViewAtCreation.indexOfChild(zoomer) == -1) {
821                             mContainerViewAtCreation.addView(zoomer);
822                         }
823                     }
824                 });
825             }
826
827             @Override
828             public void onPopupZoomerHidden(final PopupZoomer zoomer) {
829                 mContainerViewAtCreation.post(new Runnable() {
830                     @Override
831                     public void run() {
832                         if (mContainerViewAtCreation.indexOfChild(zoomer) != -1) {
833                             mContainerViewAtCreation.removeView(zoomer);
834                             mContainerViewAtCreation.invalidate();
835                         }
836                     }
837                 });
838             }
839         });
840         // TODO(yongsheng): LONG_TAP is not enabled in PopupZoomer. So need to dispatch a LONG_TAP
841         // gesture if a user completes a tap on PopupZoomer UI after a LONG_PRESS gesture.
842         PopupZoomer.OnTapListener listener = new PopupZoomer.OnTapListener() {
843             // mContainerView can change, but this OnTapListener can only be used
844             // with the mContainerViewAtCreation.
845             private final ViewGroup mContainerViewAtCreation = mContainerView;
846
847             @Override
848             public boolean onSingleTap(View v, MotionEvent e) {
849                 mContainerViewAtCreation.requestFocus();
850                 if (mNativeContentViewCore != 0) {
851                     nativeSingleTap(mNativeContentViewCore, e.getEventTime(), e.getX(), e.getY());
852                 }
853                 return true;
854             }
855
856             @Override
857             public boolean onLongPress(View v, MotionEvent e) {
858                 if (mNativeContentViewCore != 0) {
859                     nativeLongPress(mNativeContentViewCore, e.getEventTime(), e.getX(), e.getY());
860                 }
861                 return true;
862             }
863         };
864         mPopupZoomer.setOnTapListener(listener);
865     }
866
867     @VisibleForTesting
868     public void setPopupZoomerForTest(PopupZoomer popupZoomer) {
869         mPopupZoomer = popupZoomer;
870     }
871
872     /**
873      * Destroy the internal state of the ContentView. This method may only be
874      * called after the ContentView has been removed from the view system. No
875      * other methods may be called on this ContentView after this method has
876      * been called.
877      */
878     public void destroy() {
879         if (mNativeContentViewCore != 0) {
880             nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore);
881         }
882         mWebContentsObserver.detachFromWebContents();
883         mWebContentsObserver = null;
884         setSmartClipDataListener(null);
885         setZoomControlsDelegate(null);
886         // TODO(igsolla): address TODO in ContentViewClient because ContentViewClient is not
887         // currently a real Null Object.
888         //
889         // Instead of deleting the client we use the Null Object pattern to avoid null checks
890         // in this class.
891         mContentViewClient = new ContentViewClient();
892         mWebContents = null;
893         if (mViewAndroid != null) mViewAndroid.destroy();
894         mNativeContentViewCore = 0;
895         mContentSettings = null;
896         mJavaScriptInterfaces.clear();
897         mRetainedJavaScriptObjects.clear();
898         unregisterAccessibilityContentObserver();
899         mGestureStateListeners.clear();
900         ScreenOrientationListener.getInstance().removeObserver(this);
901         mPositionObserver.clearListener();
902     }
903
904     private void unregisterAccessibilityContentObserver() {
905         if (mAccessibilityScriptInjectionObserver == null) {
906             return;
907         }
908         getContext().getContentResolver().unregisterContentObserver(
909                 mAccessibilityScriptInjectionObserver);
910         mAccessibilityScriptInjectionObserver = null;
911     }
912
913     /**
914      * Returns true initially, false after destroy() has been called.
915      * It is illegal to call any other public method after destroy().
916      */
917     public boolean isAlive() {
918         return mNativeContentViewCore != 0;
919     }
920
921     /**
922      * This is only useful for passing over JNI to native code that requires ContentViewCore*.
923      * @return native ContentViewCore pointer.
924      */
925     @CalledByNative
926     public long getNativeContentViewCore() {
927         return mNativeContentViewCore;
928     }
929
930     public void setContentViewClient(ContentViewClient client) {
931         if (client == null) {
932             throw new IllegalArgumentException("The client can't be null.");
933         }
934         mContentViewClient = client;
935     }
936
937     @VisibleForTesting
938     public ContentViewClient getContentViewClient() {
939         if (mContentViewClient == null) {
940             // We use the Null Object pattern to avoid having to perform a null check in this class.
941             // We create it lazily because most of the time a client will be set almost immediately
942             // after ContentView is created.
943             mContentViewClient = new ContentViewClient();
944             // We don't set the native ContentViewClient pointer here on purpose. The native
945             // implementation doesn't mind a null delegate and using one is better than passing a
946             // Null Object, since we cut down on the number of JNI calls.
947         }
948         return mContentViewClient;
949     }
950
951     @CalledByNative
952     private void onBackgroundColorChanged(int color) {
953         getContentViewClient().onBackgroundColorChanged(color);
954     }
955
956     /**
957      * @return Viewport width in physical pixels as set from onSizeChanged.
958      */
959     @CalledByNative
960     public int getViewportWidthPix() {
961         return mViewportWidthPix;
962     }
963
964     /**
965      * @return Viewport height in physical pixels as set from onSizeChanged.
966      */
967     @CalledByNative
968     public int getViewportHeightPix() {
969         return mViewportHeightPix;
970     }
971
972     /**
973      * @return Width of underlying physical surface.
974      */
975     @CalledByNative
976     public int getPhysicalBackingWidthPix() {
977         return mPhysicalBackingWidthPix;
978     }
979
980     /**
981      * @return Height of underlying physical surface.
982      */
983     @CalledByNative
984     public int getPhysicalBackingHeightPix() {
985         return mPhysicalBackingHeightPix;
986     }
987
988     /* TODO(aelias): Remove these when downstream callers disappear. */
989     @VisibleForTesting
990     public int getViewportSizeOffsetWidthPix() {
991         return 0;
992     }
993
994     @VisibleForTesting
995     public int getViewportSizeOffsetHeightPix() {
996         return getTopControlsLayoutHeightPix();
997     }
998
999     /**
1000      * @return The amount that the viewport size given to Blink is shrunk by the URL-bar..
1001      */
1002     @CalledByNative
1003     public int getTopControlsLayoutHeightPix() {
1004         return mTopControlsLayoutHeightPix;
1005     }
1006
1007     /**
1008      * @see android.webkit.WebView#getContentHeight()
1009      */
1010     public float getContentHeightCss() {
1011         return mRenderCoordinates.getContentHeightCss();
1012     }
1013
1014     /**
1015      * @see android.webkit.WebView#getContentWidth()
1016      */
1017     public float getContentWidthCss() {
1018         return mRenderCoordinates.getContentWidthCss();
1019     }
1020
1021     /**
1022      * @return The selected text (empty if no text selected).
1023      */
1024     public String getSelectedText() {
1025         return mHasSelection ? mLastSelectedText : "";
1026     }
1027
1028     /**
1029      * @return Whether the current selection is editable (false if no text selected).
1030      */
1031     public boolean isSelectionEditable() {
1032         return mHasSelection ? mFocusedNodeEditable : false;
1033     }
1034
1035     /**
1036      * @return Whether the current focused node is editable.
1037      */
1038     public boolean isFocusedNodeEditable() {
1039         return mFocusedNodeEditable;
1040     }
1041
1042     // End FrameLayout overrides.
1043
1044     /**
1045      * @see View#onTouchEvent(MotionEvent)
1046      */
1047     public boolean onTouchEvent(MotionEvent event) {
1048         final boolean isTouchHandleEvent = false;
1049         return onTouchEventImpl(event, isTouchHandleEvent);
1050     }
1051
1052     private boolean onTouchEventImpl(MotionEvent event, boolean isTouchHandleEvent) {
1053         TraceEvent.begin("onTouchEvent");
1054         try {
1055             int eventAction = event.getActionMasked();
1056
1057             if (eventAction == MotionEvent.ACTION_DOWN) {
1058                 cancelRequestToScrollFocusedEditableNodeIntoView();
1059             }
1060
1061             if (SPenSupport.isSPenSupported(mContext))
1062                 eventAction = SPenSupport.convertSPenEventAction(eventAction);
1063             if (!isValidTouchEventActionForNative(eventAction)) return false;
1064
1065             if (mNativeContentViewCore == 0) return false;
1066
1067             // A zero offset is quite common, in which case the unnecessary copy should be avoided.
1068             MotionEvent offset = null;
1069             if (mCurrentTouchOffsetX != 0 || mCurrentTouchOffsetY != 0) {
1070                 offset = createOffsetMotionEvent(event);
1071                 event = offset;
1072             }
1073
1074             final int pointerCount = event.getPointerCount();
1075             final boolean consumed = nativeOnTouchEvent(mNativeContentViewCore, event,
1076                     event.getEventTime(), eventAction,
1077                     pointerCount, event.getHistorySize(), event.getActionIndex(),
1078                     event.getX(), event.getY(),
1079                     pointerCount > 1 ? event.getX(1) : 0,
1080                     pointerCount > 1 ? event.getY(1) : 0,
1081                     event.getPointerId(0), pointerCount > 1 ? event.getPointerId(1) : -1,
1082                     event.getTouchMajor(), pointerCount > 1 ? event.getTouchMajor(1) : 0,
1083                     event.getTouchMinor(), pointerCount > 1 ? event.getTouchMinor(1) : 0,
1084                     event.getOrientation(), pointerCount > 1 ? event.getOrientation(1) : 0,
1085                     event.getRawX(), event.getRawY(),
1086                     event.getToolType(0),
1087                     pointerCount > 1 ? event.getToolType(1) : MotionEvent.TOOL_TYPE_UNKNOWN,
1088                     event.getButtonState(),
1089                     event.getMetaState(),
1090                     isTouchHandleEvent);
1091
1092             if (offset != null) offset.recycle();
1093             return consumed;
1094         } finally {
1095             TraceEvent.end("onTouchEvent");
1096         }
1097     }
1098
1099     private static boolean isValidTouchEventActionForNative(int eventAction) {
1100         // Only these actions have any effect on gesture detection.  Other
1101         // actions have no corresponding WebTouchEvent type and may confuse the
1102         // touch pipline, so we ignore them entirely.
1103         return eventAction == MotionEvent.ACTION_DOWN
1104                 || eventAction == MotionEvent.ACTION_UP
1105                 || eventAction == MotionEvent.ACTION_CANCEL
1106                 || eventAction == MotionEvent.ACTION_MOVE
1107                 || eventAction == MotionEvent.ACTION_POINTER_DOWN
1108                 || eventAction == MotionEvent.ACTION_POINTER_UP;
1109     }
1110
1111     public void setIgnoreRemainingTouchEvents() {
1112         resetGestureDetection();
1113     }
1114
1115     public boolean isScrollInProgress() {
1116         return mTouchScrollInProgress || mPotentiallyActiveFlingCount > 0;
1117     }
1118
1119     @SuppressWarnings("unused")
1120     @CalledByNative
1121     private void onFlingStartEventConsumed(int vx, int vy) {
1122         mTouchScrollInProgress = false;
1123         mPotentiallyActiveFlingCount++;
1124         for (mGestureStateListenersIterator.rewind();
1125                     mGestureStateListenersIterator.hasNext();) {
1126             mGestureStateListenersIterator.next().onFlingStartGesture(
1127                     vx, vy, computeVerticalScrollOffset(), computeVerticalScrollExtent());
1128         }
1129     }
1130
1131     @SuppressWarnings("unused")
1132     @CalledByNative
1133     private void onFlingStartEventHadNoConsumer(int vx, int vy) {
1134         mTouchScrollInProgress = false;
1135         for (mGestureStateListenersIterator.rewind();
1136                     mGestureStateListenersIterator.hasNext();) {
1137             mGestureStateListenersIterator.next().onUnhandledFlingStartEvent(vx, vy);
1138         }
1139     }
1140
1141     @SuppressWarnings("unused")
1142     @CalledByNative
1143     private void onFlingCancelEventAck() {
1144         updateGestureStateListener(GestureEventType.FLING_CANCEL);
1145     }
1146
1147     @SuppressWarnings("unused")
1148     @CalledByNative
1149     private void onScrollBeginEventAck() {
1150         mTouchScrollInProgress = true;
1151         hidePastePopup();
1152         mZoomControlsDelegate.invokeZoomPicker();
1153         updateGestureStateListener(GestureEventType.SCROLL_START);
1154     }
1155
1156     @SuppressWarnings("unused")
1157     @CalledByNative
1158     private void onScrollUpdateGestureConsumed() {
1159         mZoomControlsDelegate.invokeZoomPicker();
1160         for (mGestureStateListenersIterator.rewind();
1161                 mGestureStateListenersIterator.hasNext();) {
1162             mGestureStateListenersIterator.next().onScrollUpdateGestureConsumed();
1163         }
1164     }
1165
1166     @SuppressWarnings("unused")
1167     @CalledByNative
1168     private void onScrollEndEventAck() {
1169         if (!mTouchScrollInProgress) return;
1170         mTouchScrollInProgress = false;
1171         updateGestureStateListener(GestureEventType.SCROLL_END);
1172     }
1173
1174     @SuppressWarnings("unused")
1175     @CalledByNative
1176     private void onPinchBeginEventAck() {
1177         updateGestureStateListener(GestureEventType.PINCH_BEGIN);
1178     }
1179
1180     @SuppressWarnings("unused")
1181     @CalledByNative
1182     private void onPinchEndEventAck() {
1183         updateGestureStateListener(GestureEventType.PINCH_END);
1184     }
1185
1186     @SuppressWarnings("unused")
1187     @CalledByNative
1188     private void onSingleTapEventAck(boolean consumed, int x, int y) {
1189         for (mGestureStateListenersIterator.rewind();
1190                 mGestureStateListenersIterator.hasNext();) {
1191             mGestureStateListenersIterator.next().onSingleTap(consumed, x, y);
1192         }
1193     }
1194
1195     /**
1196      * Called just prior to a tap or press gesture being forwarded to the renderer.
1197      */
1198     @SuppressWarnings("unused")
1199     @CalledByNative
1200     private boolean filterTapOrPressEvent(int type, int x, int y) {
1201         if (type == GestureEventType.LONG_PRESS && offerLongPressToEmbedder()) {
1202             return true;
1203         }
1204         updateForTapOrPress(type, x, y);
1205         return false;
1206     }
1207
1208     @VisibleForTesting
1209     public void sendDoubleTapForTest(long timeMs, int x, int y) {
1210         if (mNativeContentViewCore == 0) return;
1211         nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
1212     }
1213
1214     @VisibleForTesting
1215     public void flingForTest(long timeMs, int x, int y, int velocityX, int velocityY) {
1216         if (mNativeContentViewCore == 0) return;
1217         nativeFlingCancel(mNativeContentViewCore, timeMs);
1218         nativeScrollBegin(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1219         nativeFlingStart(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
1220     }
1221
1222     /**
1223      * Cancel any fling gestures active.
1224      * @param timeMs Current time (in milliseconds).
1225      */
1226     public void cancelFling(long timeMs) {
1227         if (mNativeContentViewCore == 0) return;
1228         nativeFlingCancel(mNativeContentViewCore, timeMs);
1229     }
1230
1231     /**
1232      * Add a listener that gets alerted on gesture state changes.
1233      * @param listener Listener to add.
1234      */
1235     public void addGestureStateListener(GestureStateListener listener) {
1236         mGestureStateListeners.addObserver(listener);
1237     }
1238
1239     /**
1240      * Removes a listener that was added to watch for gesture state changes.
1241      * @param listener Listener to remove.
1242      */
1243     public void removeGestureStateListener(GestureStateListener listener) {
1244         mGestureStateListeners.removeObserver(listener);
1245     }
1246
1247     void updateGestureStateListener(int gestureType) {
1248         for (mGestureStateListenersIterator.rewind();
1249                 mGestureStateListenersIterator.hasNext();) {
1250             GestureStateListener listener = mGestureStateListenersIterator.next();
1251             switch (gestureType) {
1252                 case GestureEventType.PINCH_BEGIN:
1253                     listener.onPinchStarted();
1254                     break;
1255                 case GestureEventType.PINCH_END:
1256                     listener.onPinchEnded();
1257                     break;
1258                 case GestureEventType.FLING_END:
1259                     listener.onFlingEndGesture(
1260                             computeVerticalScrollOffset(),
1261                             computeVerticalScrollExtent());
1262                     break;
1263                 case GestureEventType.FLING_CANCEL:
1264                     listener.onFlingCancelGesture();
1265                     break;
1266                 case GestureEventType.SCROLL_START:
1267                     listener.onScrollStarted(
1268                             computeVerticalScrollOffset(),
1269                             computeVerticalScrollExtent());
1270                     break;
1271                 case GestureEventType.SCROLL_END:
1272                     listener.onScrollEnded(
1273                             computeVerticalScrollOffset(),
1274                             computeVerticalScrollExtent());
1275                     break;
1276                 default:
1277                     break;
1278             }
1279         }
1280     }
1281
1282     /**
1283      * To be called when the ContentView is shown.
1284      */
1285     public void onShow() {
1286         assert mWebContents != null;
1287         mWebContents.onShow();
1288         setAccessibilityState(mAccessibilityManager.isEnabled());
1289         restoreSelectionPopupsIfNecessary();
1290     }
1291
1292     /**
1293      * @return The ID of the renderer process that backs this tab or
1294      *         {@link #INVALID_RENDER_PROCESS_PID} if there is none.
1295      */
1296     public int getCurrentRenderProcessId() {
1297         return nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1298     }
1299
1300     /**
1301      * To be called when the ContentView is hidden.
1302      */
1303     public void onHide() {
1304         assert mWebContents != null;
1305         hidePopupsAndPreserveSelection();
1306         setInjectedAccessibility(false);
1307         mWebContents.onHide();
1308     }
1309
1310     /**
1311      * Return the ContentSettings object used to retrieve the settings for this
1312      * ContentViewCore. For modifications, ChromeNativePreferences is to be used.
1313      * @return A ContentSettings object that can be used to retrieve this
1314      *         ContentViewCore's settings.
1315      */
1316     public ContentSettings getContentSettings() {
1317         return mContentSettings;
1318     }
1319
1320     private void hidePopupsAndClearSelection() {
1321         mUnselectAllOnActionModeDismiss = true;
1322         hidePopups();
1323         // Clear the selection. The selection is cleared on destroying IME
1324         // and also here since we may receive destroy first, for example
1325         // when focus is lost in webview.
1326         clearUserSelection();
1327     }
1328
1329     private void hidePopupsAndPreserveSelection() {
1330         mUnselectAllOnActionModeDismiss = false;
1331         hidePopups();
1332     }
1333
1334     private void clearUserSelection() {
1335         if (mFocusedNodeEditable) {
1336             if (mInputConnection != null) {
1337                 int selectionEnd = Selection.getSelectionEnd(mEditable);
1338                 mInputConnection.setSelection(selectionEnd, selectionEnd);
1339             }
1340         } else if (mImeAdapter != null) {
1341             mImeAdapter.unselect();
1342         }
1343     }
1344
1345     private void hidePopups() {
1346         hideSelectActionBar();
1347         hidePastePopup();
1348         hideSelectPopup();
1349         mPopupZoomer.hide(false);
1350         if (mUnselectAllOnActionModeDismiss) dismissTextHandles();
1351     }
1352
1353     private void restoreSelectionPopupsIfNecessary() {
1354         if (mHasSelection && mActionMode == null) showSelectActionBar();
1355     }
1356
1357     public void hideSelectActionBar() {
1358         if (mActionMode != null) {
1359             mActionMode.finish();
1360             mActionMode = null;
1361         }
1362     }
1363
1364     public boolean isSelectActionBarShowing() {
1365         return mActionMode != null;
1366     }
1367
1368     private void resetGestureDetection() {
1369         if (mNativeContentViewCore == 0) return;
1370         nativeResetGestureDetection(mNativeContentViewCore);
1371     }
1372
1373     /**
1374      * @see View#onAttachedToWindow()
1375      */
1376     @SuppressWarnings("javadoc")
1377     public void onAttachedToWindow() {
1378         setAccessibilityState(mAccessibilityManager.isEnabled());
1379         setTextHandlesTemporarilyHidden(false);
1380         restoreSelectionPopupsIfNecessary();
1381         ScreenOrientationListener.getInstance().addObserver(this, mContext);
1382         GamepadList.onAttachedToWindow(mContext);
1383     }
1384
1385     /**
1386      * @see View#onDetachedFromWindow()
1387      */
1388     @SuppressWarnings("javadoc")
1389     @SuppressLint("MissingSuperCall")
1390     public void onDetachedFromWindow() {
1391         setInjectedAccessibility(false);
1392         mZoomControlsDelegate.dismissZoomPicker();
1393         unregisterAccessibilityContentObserver();
1394
1395         ScreenOrientationListener.getInstance().removeObserver(this);
1396         GamepadList.onDetachedFromWindow();
1397
1398         // WebView uses PopupWindows for handle rendering, which may remain
1399         // unintentionally visible even after the WebView has been detached.
1400         // Override the handle visibility explicitly to address this, but
1401         // preserve the underlying selection for detachment cases like screen
1402         // locking and app switching.
1403         setTextHandlesTemporarilyHidden(true);
1404         hidePopupsAndPreserveSelection();
1405     }
1406
1407     /**
1408      * @see View#onVisibilityChanged(android.view.View, int)
1409      */
1410     public void onVisibilityChanged(View changedView, int visibility) {
1411         if (visibility != View.VISIBLE) {
1412             mZoomControlsDelegate.dismissZoomPicker();
1413         }
1414     }
1415
1416     /**
1417      * @see View#onCreateInputConnection(EditorInfo)
1418      */
1419     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1420         if (!mImeAdapter.hasTextInputType()) {
1421             // Although onCheckIsTextEditor will return false in this case, the EditorInfo
1422             // is still used by the InputMethodService. Need to make sure the IME doesn't
1423             // enter fullscreen mode.
1424             outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
1425         }
1426         mInputConnection = mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter,
1427                 mEditable, outAttrs);
1428         return mInputConnection;
1429     }
1430
1431     @VisibleForTesting
1432     public AdapterInputConnection getAdapterInputConnectionForTest() {
1433         return mInputConnection;
1434     }
1435
1436     @VisibleForTesting
1437     public Editable getEditableForTest() {
1438         return mEditable;
1439     }
1440
1441     /**
1442      * @see View#onCheckIsTextEditor()
1443      */
1444     public boolean onCheckIsTextEditor() {
1445         return mImeAdapter.hasTextInputType();
1446     }
1447
1448     /**
1449      * @see View#onConfigurationChanged(Configuration)
1450      */
1451     @SuppressWarnings("javadoc")
1452     public void onConfigurationChanged(Configuration newConfig) {
1453         TraceEvent.begin();
1454
1455         if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
1456             if (mNativeContentViewCore != 0) {
1457                 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
1458                         ImeAdapter.getTextInputTypeNone(), 0 /* no flags */);
1459             }
1460             mInputMethodManagerWrapper.restartInput(mContainerView);
1461         }
1462         mContainerViewInternals.super_onConfigurationChanged(newConfig);
1463
1464         // To request layout has side effect, but it seems OK as it only happen in
1465         // onConfigurationChange and layout has to be changed in most case.
1466         mContainerView.requestLayout();
1467         TraceEvent.end();
1468     }
1469
1470     /**
1471      * @see View#onSizeChanged(int, int, int, int)
1472      */
1473     @SuppressWarnings("javadoc")
1474     public void onSizeChanged(int wPix, int hPix, int owPix, int ohPix) {
1475         if (getViewportWidthPix() == wPix && getViewportHeightPix() == hPix) return;
1476
1477         mViewportWidthPix = wPix;
1478         mViewportHeightPix = hPix;
1479         if (mNativeContentViewCore != 0) {
1480             nativeWasResized(mNativeContentViewCore);
1481         }
1482
1483         updateAfterSizeChanged();
1484     }
1485
1486     /**
1487      * Called when the underlying surface the compositor draws to changes size.
1488      * This may be larger than the viewport size.
1489      */
1490     public void onPhysicalBackingSizeChanged(int wPix, int hPix) {
1491         if (mPhysicalBackingWidthPix == wPix && mPhysicalBackingHeightPix == hPix) return;
1492
1493         mPhysicalBackingWidthPix = wPix;
1494         mPhysicalBackingHeightPix = hPix;
1495
1496         if (mNativeContentViewCore != 0) {
1497             nativeWasResized(mNativeContentViewCore);
1498         }
1499     }
1500
1501     /* TODO(aelias): Remove this after downstream callers disappear. */
1502     public void onOverdrawBottomHeightChanged(int overdrawHeightPix) {
1503     }
1504
1505     private void updateAfterSizeChanged() {
1506         mPopupZoomer.hide(false);
1507
1508         // Execute a delayed form focus operation because the OSK was brought
1509         // up earlier.
1510         if (!mFocusPreOSKViewportRect.isEmpty()) {
1511             Rect rect = new Rect();
1512             getContainerView().getWindowVisibleDisplayFrame(rect);
1513             if (!rect.equals(mFocusPreOSKViewportRect)) {
1514                 // Only assume the OSK triggered the onSizeChanged if width was preserved.
1515                 if (rect.width() == mFocusPreOSKViewportRect.width()) {
1516                     assert mWebContents != null;
1517                     mWebContents.scrollFocusedEditableNodeIntoView();
1518                 }
1519                 cancelRequestToScrollFocusedEditableNodeIntoView();
1520             }
1521         }
1522     }
1523
1524     private void cancelRequestToScrollFocusedEditableNodeIntoView() {
1525         // Zero-ing the rect will prevent |updateAfterSizeChanged()| from
1526         // issuing the delayed form focus event.
1527         mFocusPreOSKViewportRect.setEmpty();
1528     }
1529
1530     /**
1531      * @see View#onWindowFocusChanged(boolean)
1532      */
1533     public void onWindowFocusChanged(boolean hasWindowFocus) {
1534         if (!hasWindowFocus) resetGestureDetection();
1535     }
1536
1537     public void onFocusChanged(boolean gainFocus) {
1538         if (gainFocus) {
1539             restoreSelectionPopupsIfNecessary();
1540         } else {
1541             hideImeIfNeeded();
1542             cancelRequestToScrollFocusedEditableNodeIntoView();
1543             if (mPreserveSelectionOnNextLossOfFocus) {
1544                 mPreserveSelectionOnNextLossOfFocus = false;
1545                 hidePopupsAndPreserveSelection();
1546             } else {
1547                 hidePopupsAndClearSelection();
1548             }
1549         }
1550         if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1551     }
1552
1553     /**
1554      * @see View#onKeyUp(int, KeyEvent)
1555      */
1556     public boolean onKeyUp(int keyCode, KeyEvent event) {
1557         if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1558             mPopupZoomer.hide(true);
1559             return true;
1560         }
1561         return mContainerViewInternals.super_onKeyUp(keyCode, event);
1562     }
1563
1564     /**
1565      * @see View#dispatchKeyEventPreIme(KeyEvent)
1566      */
1567     public boolean dispatchKeyEventPreIme(KeyEvent event) {
1568         try {
1569             TraceEvent.begin();
1570             return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1571         } finally {
1572             TraceEvent.end();
1573         }
1574     }
1575
1576     /**
1577      * @see View#dispatchKeyEvent(KeyEvent)
1578      */
1579     public boolean dispatchKeyEvent(KeyEvent event) {
1580         if (GamepadList.dispatchKeyEvent(event)) return true;
1581         if (getContentViewClient().shouldOverrideKeyEvent(event)) {
1582             return mContainerViewInternals.super_dispatchKeyEvent(event);
1583         }
1584
1585         if (mImeAdapter.dispatchKeyEvent(event)) return true;
1586
1587         return mContainerViewInternals.super_dispatchKeyEvent(event);
1588     }
1589
1590     /**
1591      * @see View#onHoverEvent(MotionEvent)
1592      * Mouse move events are sent on hover enter, hover move and hover exit.
1593      * They are sent on hover exit because sometimes it acts as both a hover
1594      * move and hover exit.
1595      */
1596     public boolean onHoverEvent(MotionEvent event) {
1597         TraceEvent.begin("onHoverEvent");
1598         MotionEvent offset = createOffsetMotionEvent(event);
1599         try {
1600             if (mBrowserAccessibilityManager != null) {
1601                 return mBrowserAccessibilityManager.onHoverEvent(offset);
1602             }
1603
1604             // Work around Android bug where the x, y coordinates of a hover exit
1605             // event are incorrect when touch exploration is on.
1606             if (mTouchExplorationEnabled && offset.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
1607                 return true;
1608             }
1609
1610             mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1611             if (mNativeContentViewCore != 0) {
1612                 nativeSendMouseMoveEvent(mNativeContentViewCore, offset.getEventTime(),
1613                         offset.getX(), offset.getY());
1614             }
1615             return true;
1616         } finally {
1617             offset.recycle();
1618             TraceEvent.end("onHoverEvent");
1619         }
1620     }
1621
1622     /**
1623      * @see View#onGenericMotionEvent(MotionEvent)
1624      */
1625     public boolean onGenericMotionEvent(MotionEvent event) {
1626         if (GamepadList.onGenericMotionEvent(event)) return true;
1627         if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1628             switch (event.getAction()) {
1629                 case MotionEvent.ACTION_SCROLL:
1630                     if (mNativeContentViewCore == 0) return false;
1631
1632                     nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
1633                             event.getX(), event.getY(),
1634                             event.getAxisValue(MotionEvent.AXIS_VSCROLL));
1635
1636                     mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1637                     // Send a delayed onMouseMove event so that we end
1638                     // up hovering over the right position after the scroll.
1639                     final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event);
1640                     mFakeMouseMoveRunnable = new Runnable() {
1641                         @Override
1642                         public void run() {
1643                             onHoverEvent(eventFakeMouseMove);
1644                             eventFakeMouseMove.recycle();
1645                         }
1646                     };
1647                     mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1648                     return true;
1649             }
1650         }
1651         return mContainerViewInternals.super_onGenericMotionEvent(event);
1652     }
1653
1654     /**
1655      * Sets the current amount to offset incoming touch events by.  This is used to handle content
1656      * moving and not lining up properly with the android input system.
1657      * @param dx The X offset in pixels to shift touch events.
1658      * @param dy The Y offset in pixels to shift touch events.
1659      */
1660     public void setCurrentMotionEventOffsets(float dx, float dy) {
1661         mCurrentTouchOffsetX = dx;
1662         mCurrentTouchOffsetY = dy;
1663     }
1664
1665     private MotionEvent createOffsetMotionEvent(MotionEvent src) {
1666         MotionEvent dst = MotionEvent.obtain(src);
1667         dst.offsetLocation(mCurrentTouchOffsetX, mCurrentTouchOffsetY);
1668         return dst;
1669     }
1670
1671     /**
1672      * @see View#scrollBy(int, int)
1673      * Currently the ContentView scrolling happens in the native side. In
1674      * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
1675      * are overridden, so that View's mScrollX and mScrollY will be unchanged at
1676      * (0, 0). This is critical for drawing ContentView correctly.
1677      */
1678     public void scrollBy(int xPix, int yPix) {
1679         if (mNativeContentViewCore != 0) {
1680             nativeScrollBy(mNativeContentViewCore,
1681                     SystemClock.uptimeMillis(), 0, 0, xPix, yPix);
1682         }
1683     }
1684
1685     /**
1686      * @see View#scrollTo(int, int)
1687      */
1688     public void scrollTo(int xPix, int yPix) {
1689         if (mNativeContentViewCore == 0) return;
1690         final float xCurrentPix = mRenderCoordinates.getScrollXPix();
1691         final float yCurrentPix = mRenderCoordinates.getScrollYPix();
1692         final float dxPix = xPix - xCurrentPix;
1693         final float dyPix = yPix - yCurrentPix;
1694         if (dxPix != 0 || dyPix != 0) {
1695             long time = SystemClock.uptimeMillis();
1696             nativeScrollBegin(mNativeContentViewCore, time,
1697                     xCurrentPix, yCurrentPix, -dxPix, -dyPix);
1698             nativeScrollBy(mNativeContentViewCore,
1699                     time, xCurrentPix, yCurrentPix, dxPix, dyPix);
1700             nativeScrollEnd(mNativeContentViewCore, time);
1701         }
1702     }
1703
1704     // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1705     //       see: b/6029133
1706     public int getNativeScrollXForTest() {
1707         return mRenderCoordinates.getScrollXPixInt();
1708     }
1709
1710     // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1711     //       see: b/6029133
1712     public int getNativeScrollYForTest() {
1713         return mRenderCoordinates.getScrollYPixInt();
1714     }
1715
1716     /**
1717      * @see View#computeHorizontalScrollExtent()
1718      */
1719     @SuppressWarnings("javadoc")
1720     public int computeHorizontalScrollExtent() {
1721         return mRenderCoordinates.getLastFrameViewportWidthPixInt();
1722     }
1723
1724     /**
1725      * @see View#computeHorizontalScrollOffset()
1726      */
1727     @SuppressWarnings("javadoc")
1728     public int computeHorizontalScrollOffset() {
1729         return mRenderCoordinates.getScrollXPixInt();
1730     }
1731
1732     /**
1733      * @see View#computeHorizontalScrollRange()
1734      */
1735     @SuppressWarnings("javadoc")
1736     public int computeHorizontalScrollRange() {
1737         return mRenderCoordinates.getContentWidthPixInt();
1738     }
1739
1740     /**
1741      * @see View#computeVerticalScrollExtent()
1742      */
1743     @SuppressWarnings("javadoc")
1744     public int computeVerticalScrollExtent() {
1745         return mRenderCoordinates.getLastFrameViewportHeightPixInt();
1746     }
1747
1748     /**
1749      * @see View#computeVerticalScrollOffset()
1750      */
1751     @SuppressWarnings("javadoc")
1752     public int computeVerticalScrollOffset() {
1753         return mRenderCoordinates.getScrollYPixInt();
1754     }
1755
1756     /**
1757      * @see View#computeVerticalScrollRange()
1758      */
1759     @SuppressWarnings("javadoc")
1760     public int computeVerticalScrollRange() {
1761         return mRenderCoordinates.getContentHeightPixInt();
1762     }
1763
1764     // End FrameLayout overrides.
1765
1766     /**
1767      * @see View#awakenScrollBars(int, boolean)
1768      */
1769     @SuppressWarnings("javadoc")
1770     public boolean awakenScrollBars(int startDelay, boolean invalidate) {
1771         // For the default implementation of ContentView which draws the scrollBars on the native
1772         // side, calling this function may get us into a bad state where we keep drawing the
1773         // scrollBars, so disable it by always returning false.
1774         if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
1775             return false;
1776         } else {
1777             return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
1778         }
1779     }
1780
1781     private void updateForTapOrPress(int type, float xPix, float yPix) {
1782         if (type != GestureEventType.SINGLE_TAP_CONFIRMED
1783                 && type != GestureEventType.SINGLE_TAP_UP
1784                 && type != GestureEventType.LONG_PRESS
1785                 && type != GestureEventType.LONG_TAP) {
1786             return;
1787         }
1788
1789         if (mContainerView.isFocusable() && mContainerView.isFocusableInTouchMode()
1790                 && !mContainerView.isFocused())  {
1791             mContainerView.requestFocus();
1792         }
1793
1794         if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
1795
1796         mLastTapX = (int) xPix;
1797         mLastTapY = (int) yPix;
1798     }
1799
1800     /**
1801      * @return The x coordinate for the last point that a tap or press gesture was initiated from.
1802      */
1803     public int getLastTapX()  {
1804         return mLastTapX;
1805     }
1806
1807     /**
1808      * @return The y coordinate for the last point that a tap or press gesture was initiated from.
1809      */
1810     public int getLastTapY()  {
1811         return mLastTapY;
1812     }
1813
1814     public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
1815         if (zoomControlsDelegate == null) {
1816             mZoomControlsDelegate = NO_OP_ZOOM_CONTROLS_DELEGATE;
1817             return;
1818         }
1819         mZoomControlsDelegate = zoomControlsDelegate;
1820     }
1821
1822     public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
1823         if (mNativeContentViewCore == 0) return;
1824         nativeSetMultiTouchZoomSupportEnabled(mNativeContentViewCore, supportsMultiTouchZoom);
1825     }
1826
1827     public void updateDoubleTapSupport(boolean supportsDoubleTap) {
1828         if (mNativeContentViewCore == 0) return;
1829         nativeSetDoubleTapSupportEnabled(mNativeContentViewCore, supportsDoubleTap);
1830     }
1831
1832     public void selectPopupMenuItems(int[] indices) {
1833         if (mNativeContentViewCore != 0) {
1834             nativeSelectPopupMenuItems(mNativeContentViewCore, mNativeSelectPopupSourceFrame,
1835                                        indices);
1836         }
1837         mNativeSelectPopupSourceFrame = 0;
1838         mSelectPopup = null;
1839     }
1840
1841     /**
1842      * Send the screen orientation value to the renderer.
1843      */
1844     @VisibleForTesting
1845     void sendOrientationChangeEvent(int orientation) {
1846         if (mNativeContentViewCore == 0) return;
1847
1848         nativeSendOrientationChangeEvent(mNativeContentViewCore, orientation);
1849     }
1850
1851     /**
1852      * Register the delegate to be used when content can not be handled by
1853      * the rendering engine, and should be downloaded instead. This will replace
1854      * the current delegate, if any.
1855      * @param delegate An implementation of ContentViewDownloadDelegate.
1856      */
1857     public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
1858         mDownloadDelegate = delegate;
1859     }
1860
1861     // Called by DownloadController.
1862     ContentViewDownloadDelegate getDownloadDelegate() {
1863         return mDownloadDelegate;
1864     }
1865
1866     @VisibleForTesting
1867     public SelectActionModeCallback.ActionHandler getSelectActionHandler() {
1868         return mActionHandler;
1869     }
1870
1871     private void showSelectActionBar() {
1872         if (mActionMode != null) {
1873             mActionMode.invalidate();
1874             return;
1875         }
1876
1877         // Start a new action mode with a SelectActionModeCallback.
1878         if (mActionHandler == null) {
1879             mActionHandler = new SelectActionModeCallback.ActionHandler() {
1880                 @Override
1881                 public void selectAll() {
1882                     mImeAdapter.selectAll();
1883                 }
1884
1885                 @Override
1886                 public void cut() {
1887                     mImeAdapter.cut();
1888                 }
1889
1890                 @Override
1891                 public void copy() {
1892                     mImeAdapter.copy();
1893                 }
1894
1895                 @Override
1896                 public void paste() {
1897                     mImeAdapter.paste();
1898                 }
1899
1900                 @Override
1901                 public void share() {
1902                     final String query = getSelectedText();
1903                     if (TextUtils.isEmpty(query)) return;
1904
1905                     Intent send = new Intent(Intent.ACTION_SEND);
1906                     send.setType("text/plain");
1907                     send.putExtra(Intent.EXTRA_TEXT, query);
1908                     try {
1909                         Intent i = Intent.createChooser(send, getContext().getString(
1910                                 R.string.actionbar_share));
1911                         i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1912                         getContext().startActivity(i);
1913                     } catch (android.content.ActivityNotFoundException ex) {
1914                         // If no app handles it, do nothing.
1915                     }
1916                 }
1917
1918                 @Override
1919                 public void search() {
1920                     final String query = getSelectedText();
1921                     if (TextUtils.isEmpty(query)) return;
1922
1923                     // See if ContentViewClient wants to override
1924                     if (getContentViewClient().doesPerformWebSearch()) {
1925                         getContentViewClient().performWebSearch(query);
1926                         return;
1927                     }
1928
1929                     Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
1930                     i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
1931                     i.putExtra(SearchManager.QUERY, query);
1932                     i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
1933                     if (!(getContext() instanceof Activity)) {
1934                         i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1935                     }
1936                     try {
1937                         getContext().startActivity(i);
1938                     } catch (android.content.ActivityNotFoundException ex) {
1939                         // If no app handles it, do nothing.
1940                     }
1941                 }
1942
1943                 @Override
1944                 public boolean isSelectionPassword() {
1945                     return mImeAdapter.isSelectionPassword();
1946                 }
1947
1948                 @Override
1949                 public boolean isSelectionEditable() {
1950                     return mFocusedNodeEditable;
1951                 }
1952
1953                 @Override
1954                 public void onDestroyActionMode() {
1955                     mActionMode = null;
1956                     if (mUnselectAllOnActionModeDismiss) {
1957                         dismissTextHandles();
1958                         clearUserSelection();
1959                     }
1960                     getContentViewClient().onContextualActionBarHidden();
1961                 }
1962
1963                 @Override
1964                 public boolean isShareAvailable() {
1965                     Intent intent = new Intent(Intent.ACTION_SEND);
1966                     intent.setType("text/plain");
1967                     return getContext().getPackageManager().queryIntentActivities(intent,
1968                             PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
1969                 }
1970
1971                 @Override
1972                 public boolean isWebSearchAvailable() {
1973                     if (getContentViewClient().doesPerformWebSearch()) return true;
1974                     Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
1975                     intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
1976                     return getContext().getPackageManager().queryIntentActivities(intent,
1977                             PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
1978                 }
1979             };
1980         }
1981         mActionMode = null;
1982         // On ICS, startActionMode throws an NPE when getParent() is null.
1983         if (mContainerView.getParent() != null) {
1984             assert mWebContents != null;
1985             mActionMode = mContainerView.startActionMode(
1986                     getContentViewClient().getSelectActionModeCallback(getContext(), mActionHandler,
1987                             mWebContents.isIncognito()));
1988         }
1989         mUnselectAllOnActionModeDismiss = true;
1990         if (mActionMode == null) {
1991             // There is no ActionMode, so remove the selection.
1992             mImeAdapter.unselect();
1993         } else {
1994             getContentViewClient().onContextualActionBarShown();
1995         }
1996     }
1997
1998     /**
1999      * Clears the current text selection.
2000      */
2001     public void clearSelection() {
2002         mImeAdapter.unselect();
2003     }
2004
2005     /**
2006      * Ensure the selection is preserved the next time the view loses focus.
2007      */
2008     public void preserveSelectionOnNextLossOfFocus() {
2009         mPreserveSelectionOnNextLossOfFocus = true;
2010     }
2011
2012     /**
2013      * @return Whether the page has an active, touch-controlled selection region.
2014      */
2015     @VisibleForTesting
2016     public boolean hasSelection() {
2017         return mHasSelection;
2018     }
2019
2020      /**
2021      * @return Whether the page has an active, touch-controlled insertion handle.
2022      */
2023     @VisibleForTesting
2024     protected boolean hasInsertion() {
2025         return mHasInsertion;
2026     }
2027
2028     private void hidePastePopup() {
2029         if (mPastePopupMenu == null) return;
2030         mPastePopupMenu.hide();
2031     }
2032
2033     @CalledByNative
2034     private void onSelectionEvent(int eventType, float posXDip, float posYDip) {
2035         switch (eventType) {
2036             case SelectionEventType.SELECTION_SHOWN:
2037                 mHasSelection = true;
2038                 mUnselectAllOnActionModeDismiss = true;
2039                 // TODO(cjhopman): Remove this when there is a better signal that long press caused
2040                 // a selection. See http://crbug.com/150151.
2041                 mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2042                 showSelectActionBar();
2043                 break;
2044
2045             case SelectionEventType.SELECTION_CLEARED:
2046                 mHasSelection = false;
2047                 mUnselectAllOnActionModeDismiss = false;
2048                 hideSelectActionBar();
2049                 break;
2050
2051             case SelectionEventType.SELECTION_DRAG_STARTED:
2052                 break;
2053
2054             case SelectionEventType.SELECTION_DRAG_STOPPED:
2055                 break;
2056
2057             case SelectionEventType.INSERTION_SHOWN:
2058                 mHasInsertion = true;
2059                 break;
2060
2061             case SelectionEventType.INSERTION_MOVED:
2062                 if (mPastePopupMenu == null) break;
2063                 if (!isScrollInProgress() && mPastePopupMenu.isShowing()) {
2064                     showPastePopup((int) posXDip, (int) posYDip);
2065                 } else {
2066                     hidePastePopup();
2067                 }
2068                 break;
2069
2070             case SelectionEventType.INSERTION_TAPPED:
2071                 if (mWasPastePopupShowingOnInsertionDragStart)
2072                     hidePastePopup();
2073                 else
2074                     showPastePopup((int) posXDip, (int) posYDip);
2075                 break;
2076
2077             case SelectionEventType.INSERTION_CLEARED:
2078                 mHasInsertion = false;
2079                 hidePastePopup();
2080                 break;
2081
2082             case SelectionEventType.INSERTION_DRAG_STARTED:
2083                 mWasPastePopupShowingOnInsertionDragStart =
2084                         mPastePopupMenu != null && mPastePopupMenu.isShowing();
2085                 hidePastePopup();
2086                 break;
2087
2088             default:
2089                 assert false : "Invalid selection event type.";
2090         }
2091
2092         final float scale = mRenderCoordinates.getDeviceScaleFactor();
2093         getContentViewClient().onSelectionEvent(eventType, posXDip * scale, posYDip * scale);
2094     }
2095
2096     private void dismissTextHandles() {
2097         mHasSelection = false;
2098         mHasInsertion = false;
2099         if (mNativeContentViewCore != 0) nativeDismissTextHandles(mNativeContentViewCore);
2100     }
2101
2102     private void setTextHandlesTemporarilyHidden(boolean hide) {
2103         if (mNativeContentViewCore == 0) return;
2104         nativeSetTextHandlesTemporarilyHidden(mNativeContentViewCore, hide);
2105     }
2106
2107     /**
2108      * Hides the IME if the containerView is the active view for IME.
2109      */
2110     public void hideImeIfNeeded() {
2111         // Hide input method window from the current view synchronously
2112         // because ImeAdapter does so asynchronouly with a delay, and
2113         // by the time when ImeAdapter dismisses the input, the
2114         // containerView may have lost focus.
2115         // We cannot trust ContentViewClient#onImeStateChangeRequested to
2116         // hide the input window because it has an empty default implementation.
2117         // So we need to explicitly hide the input method window here.
2118         if (mInputMethodManagerWrapper.isActive(mContainerView)) {
2119             mInputMethodManagerWrapper.hideSoftInputFromWindow(
2120                     mContainerView.getWindowToken(), 0, null);
2121         }
2122         getContentViewClient().onImeStateChangeRequested(false);
2123     }
2124
2125     @SuppressWarnings("unused")
2126     @CalledByNative
2127     private void updateFrameInfo(
2128             float scrollOffsetX, float scrollOffsetY,
2129             float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
2130             float contentWidth, float contentHeight,
2131             float viewportWidth, float viewportHeight,
2132             float controlsOffsetYCss, float contentOffsetYCss) {
2133         TraceEvent.begin("ContentViewCore:updateFrameInfo");
2134         // Adjust contentWidth/Height to be always at least as big as
2135         // the actual viewport (as set by onSizeChanged).
2136         final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2137         contentWidth = Math.max(contentWidth,
2138                 mViewportWidthPix / (deviceScale * pageScaleFactor));
2139         contentHeight = Math.max(contentHeight,
2140                 mViewportHeightPix / (deviceScale * pageScaleFactor));
2141         final float contentOffsetYPix = mRenderCoordinates.fromDipToPix(contentOffsetYCss);
2142
2143         final boolean contentSizeChanged =
2144                 contentWidth != mRenderCoordinates.getContentWidthCss()
2145                 || contentHeight != mRenderCoordinates.getContentHeightCss();
2146         final boolean scaleLimitsChanged =
2147                 minPageScaleFactor != mRenderCoordinates.getMinPageScaleFactor()
2148                 || maxPageScaleFactor != mRenderCoordinates.getMaxPageScaleFactor();
2149         final boolean pageScaleChanged =
2150                 pageScaleFactor != mRenderCoordinates.getPageScaleFactor();
2151         final boolean scrollChanged =
2152                 pageScaleChanged
2153                 || scrollOffsetX != mRenderCoordinates.getScrollX()
2154                 || scrollOffsetY != mRenderCoordinates.getScrollY();
2155         final boolean contentOffsetChanged =
2156                 contentOffsetYPix != mRenderCoordinates.getContentOffsetYPix();
2157
2158         final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
2159         final boolean needUpdateZoomControls = scaleLimitsChanged || scrollChanged;
2160
2161         if (needHidePopupZoomer) mPopupZoomer.hide(true);
2162
2163         if (scrollChanged) {
2164             mContainerViewInternals.onScrollChanged(
2165                     (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetX),
2166                     (int) mRenderCoordinates.fromLocalCssToPix(scrollOffsetY),
2167                     (int) mRenderCoordinates.getScrollXPix(),
2168                     (int) mRenderCoordinates.getScrollYPix());
2169         }
2170
2171         mRenderCoordinates.updateFrameInfo(
2172                 scrollOffsetX, scrollOffsetY,
2173                 contentWidth, contentHeight,
2174                 viewportWidth, viewportHeight,
2175                 pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
2176                 contentOffsetYPix);
2177
2178         if (scrollChanged || contentOffsetChanged) {
2179             for (mGestureStateListenersIterator.rewind();
2180                     mGestureStateListenersIterator.hasNext();) {
2181                 mGestureStateListenersIterator.next().onScrollOffsetOrExtentChanged(
2182                         computeVerticalScrollOffset(),
2183                         computeVerticalScrollExtent());
2184             }
2185         }
2186
2187         if (needUpdateZoomControls) mZoomControlsDelegate.updateZoomControls();
2188
2189         // Update offsets for fullscreen.
2190         final float controlsOffsetPix = controlsOffsetYCss * deviceScale;
2191         // TODO(aelias): Remove last argument after downstream removes it.
2192         getContentViewClient().onOffsetsForFullscreenChanged(
2193                 controlsOffsetPix, contentOffsetYPix, 0);
2194
2195         if (mBrowserAccessibilityManager != null) {
2196             mBrowserAccessibilityManager.notifyFrameInfoInitialized();
2197         }
2198         TraceEvent.end("ContentViewCore:updateFrameInfo");
2199     }
2200
2201     @CalledByNative
2202     private void updateImeAdapter(long nativeImeAdapterAndroid, int textInputType,
2203             int textInputFlags, String text, int selectionStart, int selectionEnd,
2204             int compositionStart, int compositionEnd, boolean showImeIfNeeded,
2205             boolean isNonImeChange) {
2206         TraceEvent.begin();
2207         mFocusedNodeEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
2208         if (!mFocusedNodeEditable) hidePastePopup();
2209
2210         mImeAdapter.updateKeyboardVisibility(
2211                 nativeImeAdapterAndroid, textInputType, textInputFlags, showImeIfNeeded);
2212
2213         if (mInputConnection != null) {
2214             mInputConnection.updateState(text, selectionStart, selectionEnd, compositionStart,
2215                     compositionEnd, isNonImeChange);
2216         }
2217
2218         if (mActionMode != null) mActionMode.invalidate();
2219         TraceEvent.end();
2220     }
2221
2222     @SuppressWarnings("unused")
2223     @CalledByNative
2224     private void setTitle(String title) {
2225         getContentViewClient().onUpdateTitle(title);
2226     }
2227
2228     /**
2229      * Called (from native) when the <select> popup needs to be shown.
2230      * @param nativeSelectPopupSourceFrame The native RenderFrameHost that owns the popup.
2231      * @param items           Items to show.
2232      * @param enabled         POPUP_ITEM_TYPEs for items.
2233      * @param multiple        Whether the popup menu should support multi-select.
2234      * @param selectedIndices Indices of selected items.
2235      */
2236     @SuppressWarnings("unused")
2237     @CalledByNative
2238     private void showSelectPopup(long nativeSelectPopupSourceFrame, Rect bounds, String[] items,
2239             int[] enabled, boolean multiple, int[] selectedIndices) {
2240         if (mContainerView.getParent() == null || mContainerView.getVisibility() != View.VISIBLE) {
2241             mNativeSelectPopupSourceFrame = nativeSelectPopupSourceFrame;
2242             selectPopupMenuItems(null);
2243             return;
2244         }
2245
2246         hidePopupsAndClearSelection();
2247         assert mNativeSelectPopupSourceFrame == 0 : "Zombie popup did not clear the frame source";
2248
2249         assert items.length == enabled.length;
2250         List<SelectPopupItem> popupItems = new ArrayList<SelectPopupItem>();
2251         for (int i = 0; i < items.length; i++) {
2252             popupItems.add(new SelectPopupItem(items[i], enabled[i]));
2253         }
2254         if (DeviceFormFactor.isTablet(mContext) && !multiple && !isTouchExplorationEnabled()) {
2255             mSelectPopup = new SelectPopupDropdown(this, popupItems, bounds, selectedIndices);
2256         } else {
2257             mSelectPopup = new SelectPopupDialog(this, popupItems, multiple, selectedIndices);
2258         }
2259         mNativeSelectPopupSourceFrame = nativeSelectPopupSourceFrame;
2260         mSelectPopup.show();
2261     }
2262
2263     /**
2264      * Called when the <select> popup needs to be hidden.
2265      */
2266     @CalledByNative
2267     private void hideSelectPopup() {
2268         if (mSelectPopup != null) mSelectPopup.hide();
2269     }
2270
2271     /**
2272      * @return The visible select popup being shown.
2273      */
2274     public SelectPopup getSelectPopupForTest() {
2275         return mSelectPopup;
2276     }
2277
2278     @SuppressWarnings("unused")
2279     @CalledByNative
2280     private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
2281         mPopupZoomer.setBitmap(zoomedBitmap);
2282         mPopupZoomer.show(targetRect);
2283     }
2284
2285     @SuppressWarnings("unused")
2286     @CalledByNative
2287     private TouchEventSynthesizer createTouchEventSynthesizer() {
2288         return new TouchEventSynthesizer(this);
2289     }
2290
2291     @SuppressWarnings("unused")
2292     @CalledByNative
2293     private PopupTouchHandleDrawable createPopupTouchHandleDrawable() {
2294         if (mTouchHandleDelegate == null) {
2295             mTouchHandleDelegate = new PopupTouchHandleDrawableDelegate() {
2296                 @Override
2297                 public View getParent() {
2298                     return getContainerView();
2299                 }
2300
2301                 @Override
2302                 public PositionObserver getParentPositionObserver() {
2303                     return mPositionObserver;
2304                 }
2305
2306                 @Override
2307                 public boolean onTouchHandleEvent(MotionEvent event) {
2308                     final boolean isTouchHandleEvent = true;
2309                     return onTouchEventImpl(event, isTouchHandleEvent);
2310                 }
2311
2312                 @Override
2313                 public boolean isScrollInProgress() {
2314                     return ContentViewCore.this.isScrollInProgress();
2315                 }
2316             };
2317         }
2318         return new PopupTouchHandleDrawable(mTouchHandleDelegate);
2319     }
2320
2321     @SuppressWarnings("unused")
2322     @CalledByNative
2323     private void onSelectionChanged(String text) {
2324         mLastSelectedText = text;
2325         getContentViewClient().onSelectionChanged(text);
2326     }
2327
2328     @SuppressWarnings("unused")
2329     @CalledByNative
2330     private void showPastePopupWithFeedback(int xDip, int yDip) {
2331         // TODO(jdduke): Remove this when there is a better signal that long press caused
2332         // showing of the paste popup. See http://crbug.com/150151.
2333         if (showPastePopup(xDip, yDip)) {
2334             mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2335         }
2336     }
2337
2338     private boolean showPastePopup(int xDip, int yDip) {
2339         if (!mHasInsertion || !canPaste()) return false;
2340         final float contentOffsetYPix = mRenderCoordinates.getContentOffsetYPix();
2341         getPastePopup().showAt(
2342                 (int) mRenderCoordinates.fromDipToPix(xDip),
2343                 (int) (mRenderCoordinates.fromDipToPix(yDip) + contentOffsetYPix));
2344         return true;
2345     }
2346
2347     private PastePopupMenu getPastePopup() {
2348         if (mPastePopupMenu == null) {
2349             mPastePopupMenu = new PastePopupMenu(getContainerView(),
2350                 new PastePopupMenuDelegate() {
2351                     @Override
2352                     public void paste() {
2353                         mImeAdapter.paste();
2354                         dismissTextHandles();
2355                     }
2356                 });
2357         }
2358         return mPastePopupMenu;
2359     }
2360
2361     @VisibleForTesting
2362     public PastePopupMenu getPastePopupForTest() {
2363         return getPastePopup();
2364     }
2365
2366     private boolean canPaste() {
2367         if (!mFocusedNodeEditable) return false;
2368         return ((ClipboardManager) mContext.getSystemService(
2369                 Context.CLIPBOARD_SERVICE)).hasPrimaryClip();
2370     }
2371
2372     @SuppressWarnings("unused")
2373     @CalledByNative
2374     private void onRenderProcessChange() {
2375         attachImeAdapter();
2376     }
2377
2378     /**
2379      * Attaches the native ImeAdapter object to the java ImeAdapter to allow communication via JNI.
2380      */
2381     public void attachImeAdapter() {
2382         if (mImeAdapter != null && mNativeContentViewCore != 0) {
2383             mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2384         }
2385     }
2386
2387     /**
2388      * @see View#hasFocus()
2389      */
2390     @CalledByNative
2391     private boolean hasFocus() {
2392         // If the container view is not focusable, we consider it always focused from
2393         // Chromium's point of view.
2394         if (!mContainerView.isFocusable()) return true;
2395         return mContainerView.hasFocus();
2396     }
2397
2398     /**
2399      * Checks whether the ContentViewCore can be zoomed in.
2400      *
2401      * @return True if the ContentViewCore can be zoomed in.
2402      */
2403     // This method uses the term 'zoom' for legacy reasons, but relates
2404     // to what chrome calls the 'page scale factor'.
2405     public boolean canZoomIn() {
2406         final float zoomInExtent = mRenderCoordinates.getMaxPageScaleFactor()
2407                 - mRenderCoordinates.getPageScaleFactor();
2408         return zoomInExtent > ZOOM_CONTROLS_EPSILON;
2409     }
2410
2411     /**
2412      * Checks whether the ContentViewCore can be zoomed out.
2413      *
2414      * @return True if the ContentViewCore can be zoomed out.
2415      */
2416     // This method uses the term 'zoom' for legacy reasons, but relates
2417     // to what chrome calls the 'page scale factor'.
2418     public boolean canZoomOut() {
2419         final float zoomOutExtent = mRenderCoordinates.getPageScaleFactor()
2420                 - mRenderCoordinates.getMinPageScaleFactor();
2421         return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
2422     }
2423
2424     /**
2425      * Zooms in the ContentViewCore by 25% (or less if that would result in
2426      * zooming in more than possible).
2427      *
2428      * @return True if there was a zoom change, false otherwise.
2429      */
2430     // This method uses the term 'zoom' for legacy reasons, but relates
2431     // to what chrome calls the 'page scale factor'.
2432     public boolean zoomIn() {
2433         if (!canZoomIn()) {
2434             return false;
2435         }
2436         return pinchByDelta(1.25f);
2437     }
2438
2439     /**
2440      * Zooms out the ContentViewCore by 20% (or less if that would result in
2441      * zooming out more than possible).
2442      *
2443      * @return True if there was a zoom change, false otherwise.
2444      */
2445     // This method uses the term 'zoom' for legacy reasons, but relates
2446     // to what chrome calls the 'page scale factor'.
2447     public boolean zoomOut() {
2448         if (!canZoomOut()) {
2449             return false;
2450         }
2451         return pinchByDelta(0.8f);
2452     }
2453
2454     /**
2455      * Resets the zoom factor of the ContentViewCore.
2456      *
2457      * @return True if there was a zoom change, false otherwise.
2458      */
2459     // This method uses the term 'zoom' for legacy reasons, but relates
2460     // to what chrome calls the 'page scale factor'.
2461     public boolean zoomReset() {
2462         // The page scale factor is initialized to mNativeMinimumScale when
2463         // the page finishes loading. Thus sets it back to mNativeMinimumScale.
2464         if (!canZoomOut()) return false;
2465         return pinchByDelta(
2466                 mRenderCoordinates.getMinPageScaleFactor()
2467                         / mRenderCoordinates.getPageScaleFactor());
2468     }
2469
2470     /**
2471      * Simulate a pinch zoom gesture.
2472      *
2473      * @param delta the factor by which the current page scale should be multiplied by.
2474      * @return whether the gesture was sent.
2475      */
2476     public boolean pinchByDelta(float delta) {
2477         if (mNativeContentViewCore == 0) return false;
2478
2479         long timeMs = SystemClock.uptimeMillis();
2480         int xPix = getViewportWidthPix() / 2;
2481         int yPix = getViewportHeightPix() / 2;
2482
2483         nativePinchBegin(mNativeContentViewCore, timeMs, xPix, yPix);
2484         nativePinchBy(mNativeContentViewCore, timeMs, xPix, yPix, delta);
2485         nativePinchEnd(mNativeContentViewCore, timeMs);
2486
2487         return true;
2488     }
2489
2490     /**
2491      * Invokes the graphical zoom picker widget for this ContentView.
2492      */
2493     public void invokeZoomPicker() {
2494         mZoomControlsDelegate.invokeZoomPicker();
2495     }
2496
2497     /**
2498      * Enables or disables inspection of JavaScript objects added via
2499      * {@link #addJavascriptInterface(Object, String)} by means of Object.keys() method and
2500      * &quot;for .. in&quot; loop. Being able to inspect JavaScript objects is useful
2501      * when debugging hybrid Android apps, but can't be enabled for legacy applications due
2502      * to compatibility risks.
2503      *
2504      * @param allow Whether to allow JavaScript objects inspection.
2505      */
2506     public void setAllowJavascriptInterfacesInspection(boolean allow) {
2507         nativeSetAllowJavascriptInterfacesInspection(mNativeContentViewCore, allow);
2508     }
2509
2510     /**
2511      * Returns JavaScript interface objects previously injected via
2512      * {@link #addJavascriptInterface(Object, String)}.
2513      *
2514      * @return the mapping of names to interface objects and corresponding annotation classes
2515      */
2516     public Map<String, Pair<Object, Class>> getJavascriptInterfaces() {
2517         return mJavaScriptInterfaces;
2518     }
2519
2520     /**
2521      * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
2522      * and automatically pass in {@link JavascriptInterface} as the required annotation.
2523      *
2524      * @param object The Java object to inject into the ContentViewCore's JavaScript context.  Null
2525      *               values are ignored.
2526      * @param name   The name used to expose the instance in JavaScript.
2527      */
2528     public void addJavascriptInterface(Object object, String name) {
2529         addPossiblyUnsafeJavascriptInterface(object, name, JavascriptInterface.class);
2530     }
2531
2532     /**
2533      * This method injects the supplied Java object into the ContentViewCore.
2534      * The object is injected into the JavaScript context of the main frame,
2535      * using the supplied name. This allows the Java object to be accessed from
2536      * JavaScript. Note that that injected objects will not appear in
2537      * JavaScript until the page is next (re)loaded. For example:
2538      * <pre> view.addJavascriptInterface(new Object(), "injectedObject");
2539      * view.loadData("<!DOCTYPE html><title></title>", "text/html", null);
2540      * view.loadUrl("javascript:alert(injectedObject.toString())");</pre>
2541      * <p><strong>IMPORTANT:</strong>
2542      * <ul>
2543      * <li> addJavascriptInterface() can be used to allow JavaScript to control
2544      * the host application. This is a powerful feature, but also presents a
2545      * security risk. Use of this method in a ContentViewCore containing
2546      * untrusted content could allow an attacker to manipulate the host
2547      * application in unintended ways, executing Java code with the permissions
2548      * of the host application. Use extreme care when using this method in a
2549      * ContentViewCore which could contain untrusted content. Particular care
2550      * should be taken to avoid unintentional access to inherited methods, such
2551      * as {@link Object#getClass()}. To prevent access to inherited methods,
2552      * pass an annotation for {@code requiredAnnotation}.  This will ensure
2553      * that only methods with {@code requiredAnnotation} are exposed to the
2554      * Javascript layer.  {@code requiredAnnotation} will be passed to all
2555      * subsequently injected Java objects if any methods return an object.  This
2556      * means the same restrictions (or lack thereof) will apply.  Alternatively,
2557      * {@link #addJavascriptInterface(Object, String)} can be called, which
2558      * automatically uses the {@link JavascriptInterface} annotation.
2559      * <li> JavaScript interacts with Java objects on a private, background
2560      * thread of the ContentViewCore. Care is therefore required to maintain
2561      * thread safety.</li>
2562      * </ul></p>
2563      *
2564      * @param object             The Java object to inject into the
2565      *                           ContentViewCore's JavaScript context. Null
2566      *                           values are ignored.
2567      * @param name               The name used to expose the instance in
2568      *                           JavaScript.
2569      * @param requiredAnnotation Restrict exposed methods to ones with this
2570      *                           annotation.  If {@code null} all methods are
2571      *                           exposed.
2572      *
2573      */
2574     public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
2575             Class<? extends Annotation> requiredAnnotation) {
2576         if (mNativeContentViewCore != 0 && object != null) {
2577             mJavaScriptInterfaces.put(name, new Pair<Object, Class>(object, requiredAnnotation));
2578             nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation);
2579         }
2580     }
2581
2582     /**
2583      * Removes a previously added JavaScript interface with the given name.
2584      *
2585      * @param name The name of the interface to remove.
2586      */
2587     public void removeJavascriptInterface(String name) {
2588         mJavaScriptInterfaces.remove(name);
2589         if (mNativeContentViewCore != 0) {
2590             nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2591         }
2592     }
2593
2594     /**
2595      * Return the current scale of the ContentView.
2596      * @return The current page scale factor.
2597      */
2598     @VisibleForTesting
2599     public float getScale() {
2600         return mRenderCoordinates.getPageScaleFactor();
2601     }
2602
2603     @CalledByNative
2604     private void startContentIntent(String contentUrl) {
2605         getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2606     }
2607
2608     @Override
2609     public void onAccessibilityStateChanged(boolean enabled) {
2610         setAccessibilityState(enabled);
2611     }
2612
2613     /**
2614      * Determines whether or not this ContentViewCore can handle this accessibility action.
2615      * @param action The action to perform.
2616      * @return Whether or not this action is supported.
2617      */
2618     public boolean supportsAccessibilityAction(int action) {
2619         return mAccessibilityInjector.supportsAccessibilityAction(action);
2620     }
2621
2622     /**
2623      * Attempts to perform an accessibility action on the web content.  If the accessibility action
2624      * cannot be processed, it returns {@code null}, allowing the caller to know to call the
2625      * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value.
2626      * Otherwise the return value from this method should be used.
2627      * @param action The action to perform.
2628      * @param arguments Optional action arguments.
2629      * @return Whether the action was performed or {@code null} if the call should be delegated to
2630      *         the super {@link View} class.
2631      */
2632     public boolean performAccessibilityAction(int action, Bundle arguments) {
2633         if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2634             return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2635         }
2636
2637         return false;
2638     }
2639
2640     /**
2641      * Set the BrowserAccessibilityManager, used for native accessibility
2642      * (not script injection). This is only set when system accessibility
2643      * has been enabled.
2644      * @param manager The new BrowserAccessibilityManager.
2645      */
2646     public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
2647         mBrowserAccessibilityManager = manager;
2648     }
2649
2650     /**
2651      * Get the BrowserAccessibilityManager, used for native accessibility
2652      * (not script injection). This will return null when system accessibility
2653      * is not enabled.
2654      * @return This view's BrowserAccessibilityManager.
2655      */
2656     public BrowserAccessibilityManager getBrowserAccessibilityManager() {
2657         return mBrowserAccessibilityManager;
2658     }
2659
2660     /**
2661      * If native accessibility (not script injection) is enabled, and if this is
2662      * running on JellyBean or later, returns an AccessibilityNodeProvider that
2663      * implements native accessibility for this view. Returns null otherwise.
2664      * Lazily initializes native accessibility here if it's allowed.
2665      * @return The AccessibilityNodeProvider, if available, or null otherwise.
2666      */
2667     public AccessibilityNodeProvider getAccessibilityNodeProvider() {
2668         if (mBrowserAccessibilityManager != null) {
2669             return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
2670         }
2671
2672         if (mNativeAccessibilityAllowed && !mNativeAccessibilityEnabled
2673                 && mNativeContentViewCore != 0
2674                 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
2675             mNativeAccessibilityEnabled = true;
2676             nativeSetAccessibilityEnabled(mNativeContentViewCore, true);
2677         }
2678
2679         return null;
2680     }
2681
2682     /**
2683      * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
2684      */
2685     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2686         // Note: this is only used by the script-injecting accessibility code.
2687         mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
2688     }
2689
2690     /**
2691      * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
2692      */
2693     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
2694         // Note: this is only used by the script-injecting accessibility code.
2695         event.setClassName(this.getClass().getName());
2696
2697         // Identify where the top-left of the screen currently points to.
2698         event.setScrollX(mRenderCoordinates.getScrollXPixInt());
2699         event.setScrollY(mRenderCoordinates.getScrollYPixInt());
2700
2701         // The maximum scroll values are determined by taking the content dimensions and
2702         // subtracting off the actual dimensions of the ChromeView.
2703         int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt());
2704         int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt());
2705         event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0);
2706
2707         // Setting the maximum scroll values requires API level 15 or higher.
2708         final int sdkVersionRequiredToSetScroll = 15;
2709         if (Build.VERSION.SDK_INT >= sdkVersionRequiredToSetScroll) {
2710             event.setMaxScrollX(maxScrollXPix);
2711             event.setMaxScrollY(maxScrollYPix);
2712         }
2713     }
2714
2715     /**
2716      * Returns whether accessibility script injection is enabled on the device
2717      */
2718     public boolean isDeviceAccessibilityScriptInjectionEnabled() {
2719         try {
2720             // On JellyBean and higher, native accessibility is the default so script
2721             // injection is only allowed if enabled via a flag.
2722             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
2723                     && !CommandLine.getInstance().hasSwitch(
2724                             ContentSwitches.ENABLE_ACCESSIBILITY_SCRIPT_INJECTION)) {
2725                 return false;
2726             }
2727
2728             if (!mContentSettings.getJavaScriptEnabled()) {
2729                 return false;
2730             }
2731
2732             int result = getContext().checkCallingOrSelfPermission(
2733                     android.Manifest.permission.INTERNET);
2734             if (result != PackageManager.PERMISSION_GRANTED) {
2735                 return false;
2736             }
2737
2738             Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
2739             field.setAccessible(true);
2740             String accessibilityScriptInjection = (String) field.get(null);
2741             ContentResolver contentResolver = getContext().getContentResolver();
2742
2743             if (mAccessibilityScriptInjectionObserver == null) {
2744                 ContentObserver contentObserver = new ContentObserver(new Handler()) {
2745                     @Override
2746                     public void onChange(boolean selfChange, Uri uri) {
2747                         setAccessibilityState(mAccessibilityManager.isEnabled());
2748                     }
2749                 };
2750                 contentResolver.registerContentObserver(
2751                         Settings.Secure.getUriFor(accessibilityScriptInjection),
2752                         false,
2753                         contentObserver);
2754                 mAccessibilityScriptInjectionObserver = contentObserver;
2755             }
2756
2757             return Settings.Secure.getInt(contentResolver, accessibilityScriptInjection, 0) == 1;
2758         } catch (NoSuchFieldException e) {
2759             // Do nothing, default to false.
2760         } catch (IllegalAccessException e) {
2761             // Do nothing, default to false.
2762         }
2763         return false;
2764     }
2765
2766     /**
2767      * Returns whether or not accessibility injection is being used.
2768      */
2769     public boolean isInjectingAccessibilityScript() {
2770         return mAccessibilityInjector.accessibilityIsAvailable();
2771     }
2772
2773     /**
2774      * Returns true if accessibility is on and touch exploration is enabled.
2775      */
2776     public boolean isTouchExplorationEnabled() {
2777         return mTouchExplorationEnabled;
2778     }
2779
2780     /**
2781      * Turns browser accessibility on or off.
2782      * If |state| is |false|, this turns off both native and injected accessibility.
2783      * Otherwise, if accessibility script injection is enabled, this will enable the injected
2784      * accessibility scripts. Native accessibility is enabled on demand.
2785      */
2786     public void setAccessibilityState(boolean state) {
2787         if (!state) {
2788             setInjectedAccessibility(false);
2789             mNativeAccessibilityAllowed = false;
2790             mTouchExplorationEnabled = false;
2791         } else {
2792             boolean useScriptInjection = isDeviceAccessibilityScriptInjectionEnabled();
2793             setInjectedAccessibility(useScriptInjection);
2794             mNativeAccessibilityAllowed = !useScriptInjection;
2795             mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
2796         }
2797     }
2798
2799     /**
2800      * Enable or disable injected accessibility features
2801      */
2802     public void setInjectedAccessibility(boolean enabled) {
2803         mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
2804         mAccessibilityInjector.setScriptEnabled(enabled);
2805     }
2806
2807     /**
2808      * Stop any TTS notifications that are currently going on.
2809      */
2810     public void stopCurrentAccessibilityNotifications() {
2811         mAccessibilityInjector.onPageLostFocus();
2812     }
2813
2814     /**
2815      * Return whether or not we should set accessibility focus on page load.
2816      */
2817     public boolean shouldSetAccessibilityFocusOnPageLoad() {
2818         return mShouldSetAccessibilityFocusOnPageLoad;
2819     }
2820
2821     /**
2822      * Sets whether or not we should set accessibility focus on page load.
2823      * This only applies if an accessibility service like TalkBack is running.
2824      * This is desirable behavior for a browser window, but not for an embedded
2825      * WebView.
2826      */
2827     public void setShouldSetAccessibilityFocusOnPageLoad(boolean on) {
2828         mShouldSetAccessibilityFocusOnPageLoad = on;
2829     }
2830
2831     /**
2832      *
2833      * @return The cached copy of render positions and scales.
2834      */
2835     public RenderCoordinates getRenderCoordinates() {
2836         return mRenderCoordinates;
2837     }
2838
2839     @CalledByNative
2840     private static Rect createRect(int x, int y, int right, int bottom) {
2841         return new Rect(x, y, right, bottom);
2842     }
2843
2844     public void extractSmartClipData(int x, int y, int width, int height) {
2845         if (mNativeContentViewCore != 0) {
2846             x += mSmartClipOffsetX;
2847             y += mSmartClipOffsetY;
2848             nativeExtractSmartClipData(mNativeContentViewCore, x, y, width, height);
2849         }
2850     }
2851
2852     /**
2853      * Set offsets for smart clip.
2854      *
2855      * <p>This should be called if there is a viewport change introduced by,
2856      * e.g., show and hide of a location bar.
2857      *
2858      * @param offsetX Offset for X position.
2859      * @param offsetY Offset for Y position.
2860      */
2861     public void setSmartClipOffsets(int offsetX, int offsetY) {
2862         mSmartClipOffsetX = offsetX;
2863         mSmartClipOffsetY = offsetY;
2864     }
2865
2866     @CalledByNative
2867     private void onSmartClipDataExtracted(String text, String html, Rect clipRect) {
2868         // Translate the positions by the offsets introduced by location bar. Note that the
2869         // coordinates are in dp scale, and that this definitely has the potential to be
2870         // different from the offsets when extractSmartClipData() was called. However,
2871         // as long as OEM has a UI that consumes all the inputs and waits until the
2872         // callback is called, then there shouldn't be any difference.
2873         // TODO(changwan): once crbug.com/416432 is resolved, try to pass offsets as
2874         // separate params for extractSmartClipData(), and apply them not the new offset
2875         // values in the callback.
2876         final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2877         final int offsetXInDp = (int) (mSmartClipOffsetX / deviceScale);
2878         final int offsetYInDp = (int) (mSmartClipOffsetY / deviceScale);
2879         clipRect.offset(-offsetXInDp, -offsetYInDp);
2880
2881         if (mSmartClipDataListener != null) {
2882             mSmartClipDataListener.onSmartClipDataExtracted(text, html, clipRect);
2883         }
2884     }
2885
2886     public void setSmartClipDataListener(SmartClipDataListener listener) {
2887         mSmartClipDataListener = listener;
2888     }
2889
2890     public void setBackgroundOpaque(boolean opaque) {
2891         if (mNativeContentViewCore != 0) {
2892             nativeSetBackgroundOpaque(mNativeContentViewCore, opaque);
2893         }
2894     }
2895
2896     /**
2897      * Offer a long press gesture to the embedding View, primarily for WebView compatibility.
2898      *
2899      * @return true if the embedder handled the event.
2900      */
2901     private boolean offerLongPressToEmbedder() {
2902         return mContainerView.performLongClick();
2903     }
2904
2905     /**
2906      * Reset scroll and fling accounting, notifying listeners as appropriate.
2907      * This is useful as a failsafe when the input stream may have been interruped.
2908      */
2909     private void resetScrollInProgress() {
2910         if (!isScrollInProgress()) return;
2911
2912         final boolean touchScrollInProgress = mTouchScrollInProgress;
2913         final int potentiallyActiveFlingCount = mPotentiallyActiveFlingCount;
2914
2915         mTouchScrollInProgress = false;
2916         mPotentiallyActiveFlingCount = 0;
2917
2918         if (touchScrollInProgress) updateGestureStateListener(GestureEventType.SCROLL_END);
2919         if (potentiallyActiveFlingCount > 0) updateGestureStateListener(GestureEventType.FLING_END);
2920     }
2921
2922     private native long nativeInit(long webContentsPtr,
2923             long viewAndroidPtr, long windowAndroidPtr, HashSet<Object> retainedObjectSet);
2924
2925     ContentVideoViewClient getContentVideoViewClient() {
2926         return getContentViewClient().getContentVideoViewClient();
2927     }
2928
2929     @CalledByNative
2930     private boolean shouldBlockMediaRequest(String url) {
2931         return getContentViewClient().shouldBlockMediaRequest(url);
2932     }
2933
2934     @CalledByNative
2935     private void onNativeFlingStopped() {
2936         // Note that mTouchScrollInProgress should normally be false at this
2937         // point, but we reset it anyway as another failsafe.
2938         mTouchScrollInProgress = false;
2939         if (mPotentiallyActiveFlingCount <= 0) return;
2940         mPotentiallyActiveFlingCount--;
2941         updateGestureStateListener(GestureEventType.FLING_END);
2942     }
2943
2944     @Override
2945     public void onScreenOrientationChanged(int orientation) {
2946         sendOrientationChangeEvent(orientation);
2947     }
2948
2949     /**
2950      * Set whether the ContentViewCore requires the WebContents to be fullscreen in order to lock
2951      * the screen orientation.
2952      */
2953     public void setFullscreenRequiredForOrientationLock(boolean value) {
2954         mFullscreenRequiredForOrientationLock = value;
2955     }
2956
2957     @CalledByNative
2958     private boolean isFullscreenRequiredForOrientationLock() {
2959         return mFullscreenRequiredForOrientationLock;
2960     }
2961
2962     private native WebContents nativeGetWebContentsAndroid(long nativeContentViewCoreImpl);
2963
2964     private native void nativeOnJavaContentViewCoreDestroyed(long nativeContentViewCoreImpl);
2965
2966     private native void nativeSetFocus(long nativeContentViewCoreImpl, boolean focused);
2967
2968     private native void nativeSendOrientationChangeEvent(
2969             long nativeContentViewCoreImpl, int orientation);
2970
2971     // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
2972     private native boolean nativeOnTouchEvent(
2973             long nativeContentViewCoreImpl, MotionEvent event,
2974             long timeMs, int action, int pointerCount, int historySize, int actionIndex,
2975             float x0, float y0, float x1, float y1,
2976             int pointerId0, int pointerId1,
2977             float touchMajor0, float touchMajor1,
2978             float touchMinor0, float touchMinor1,
2979             float orientation0, float orientation1,
2980             float rawX, float rawY,
2981             int androidToolType0, int androidToolType1,
2982             int androidButtonState, int androidMetaState,
2983             boolean isTouchHandleEvent);
2984
2985     private native int nativeSendMouseMoveEvent(
2986             long nativeContentViewCoreImpl, long timeMs, float x, float y);
2987
2988     private native int nativeSendMouseWheelEvent(
2989             long nativeContentViewCoreImpl, long timeMs, float x, float y, float verticalAxis);
2990
2991     private native void nativeScrollBegin(
2992             long nativeContentViewCoreImpl, long timeMs, float x, float y, float hintX,
2993             float hintY);
2994
2995     private native void nativeScrollEnd(long nativeContentViewCoreImpl, long timeMs);
2996
2997     private native void nativeScrollBy(
2998             long nativeContentViewCoreImpl, long timeMs, float x, float y,
2999             float deltaX, float deltaY);
3000
3001     private native void nativeFlingStart(
3002             long nativeContentViewCoreImpl, long timeMs, float x, float y, float vx, float vy);
3003
3004     private native void nativeFlingCancel(long nativeContentViewCoreImpl, long timeMs);
3005
3006     private native void nativeSingleTap(
3007             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3008
3009     private native void nativeDoubleTap(
3010             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3011
3012     private native void nativeLongPress(
3013             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3014
3015     private native void nativePinchBegin(
3016             long nativeContentViewCoreImpl, long timeMs, float x, float y);
3017
3018     private native void nativePinchEnd(long nativeContentViewCoreImpl, long timeMs);
3019
3020     private native void nativePinchBy(long nativeContentViewCoreImpl, long timeMs,
3021             float anchorX, float anchorY, float deltaScale);
3022
3023     private native void nativeSelectBetweenCoordinates(
3024             long nativeContentViewCoreImpl, float x1, float y1, float x2, float y2);
3025
3026     private native void nativeMoveCaret(long nativeContentViewCoreImpl, float x, float y);
3027
3028     private native void nativeDismissTextHandles(long nativeContentViewCoreImpl);
3029     private native void nativeSetTextHandlesTemporarilyHidden(
3030             long nativeContentViewCoreImpl, boolean hidden);
3031
3032     private native void nativeResetGestureDetection(long nativeContentViewCoreImpl);
3033
3034     private native void nativeSetDoubleTapSupportEnabled(
3035             long nativeContentViewCoreImpl, boolean enabled);
3036
3037     private native void nativeSetMultiTouchZoomSupportEnabled(
3038             long nativeContentViewCoreImpl, boolean enabled);
3039
3040     private native void nativeSelectPopupMenuItems(long nativeContentViewCoreImpl,
3041             long nativeSelectPopupSourceFrame, int[] indices);
3042
3043
3044     private native long nativeGetNativeImeAdapter(long nativeContentViewCoreImpl);
3045
3046     private native int nativeGetCurrentRenderProcessId(long nativeContentViewCoreImpl);
3047
3048     private native void nativeSetAllowJavascriptInterfacesInspection(
3049             long nativeContentViewCoreImpl, boolean allow);
3050
3051     private native void nativeAddJavascriptInterface(long nativeContentViewCoreImpl, Object object,
3052             String name, Class requiredAnnotation);
3053
3054     private native void nativeRemoveJavascriptInterface(long nativeContentViewCoreImpl,
3055             String name);
3056
3057     private native void nativeWasResized(long nativeContentViewCoreImpl);
3058
3059     private native void nativeSetAccessibilityEnabled(
3060             long nativeContentViewCoreImpl, boolean enabled);
3061
3062     private native void nativeExtractSmartClipData(long nativeContentViewCoreImpl,
3063             int x, int y, int w, int h);
3064
3065     private native void nativeSetBackgroundOpaque(long nativeContentViewCoreImpl, boolean opaque);
3066 }