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