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