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