ContentViewCore.java revision a36e5920737c6adbddd3e43b760e5de8431db6e0
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                    android.widget.AbsoluteLayout.LayoutParams lp =
535                            new android.widget.AbsoluteLayout.LayoutParams((int)width,
536                                    (int)height, leftMargin, topMargin);
537                    view.setLayoutParams(lp);
538                } else {
539                    Log.e(TAG, "Unknown layout " + mContainerView.getClass().getName());
540                }
541            }
542
543            @Override
544            public void releaseAnchorView(View anchorView) {
545                mContainerView.removeView(anchorView);
546            }
547        };
548    }
549
550    @VisibleForTesting
551    public ImeAdapter getImeAdapterForTest() {
552        return mImeAdapter;
553    }
554
555    @VisibleForTesting
556    public void setAdapterInputConnectionFactory(AdapterInputConnectionFactory factory) {
557        mAdapterInputConnectionFactory = factory;
558    }
559
560    @VisibleForTesting
561    public AdapterInputConnection getInputConnectionForTest() {
562        return mInputConnection;
563    }
564
565    private ImeAdapter createImeAdapter(Context context) {
566        return new ImeAdapter(new InputMethodManagerWrapper(context),
567                new ImeAdapter.ImeAdapterDelegate() {
568                    @Override
569                    public void onImeEvent(boolean isFinish) {
570                        getContentViewClient().onImeEvent();
571                        if (!isFinish) {
572                            hideHandles();
573                            undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
574                        }
575                    }
576
577                    @Override
578                    public void onSetFieldValue() {
579                        scrollFocusedEditableNodeIntoView();
580                    }
581
582                    @Override
583                    public void onDismissInput() {
584                        getContentViewClient().onImeStateChangeRequested(false);
585                    }
586
587                    @Override
588                    public View getAttachedView() {
589                        return mContainerView;
590                    }
591
592                    @Override
593                    public ResultReceiver getNewShowKeyboardReceiver() {
594                        return new ResultReceiver(new Handler()) {
595                            @Override
596                            public void onReceiveResult(int resultCode, Bundle resultData) {
597                                getContentViewClient().onImeStateChangeRequested(
598                                        resultCode == InputMethodManager.RESULT_SHOWN ||
599                                        resultCode == InputMethodManager.RESULT_UNCHANGED_SHOWN);
600                                if (resultCode == InputMethodManager.RESULT_SHOWN) {
601                                    // If OSK is newly shown, delay the form focus until
602                                    // the onSizeChanged (in order to adjust relative to the
603                                    // new size).
604                                    getContainerView().getWindowVisibleDisplayFrame(
605                                            mFocusPreOSKViewportRect);
606                                } else if (resultCode ==
607                                        InputMethodManager.RESULT_UNCHANGED_SHOWN) {
608                                    // If the OSK was already there, focus the form immediately.
609                                    scrollFocusedEditableNodeIntoView();
610                                } else {
611                                    undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
612                                }
613                            }
614                        };
615                    }
616                }
617        );
618    }
619
620    /**
621     * Returns true if the given Activity has hardware acceleration enabled
622     * in its manifest, or in its foreground window.
623     *
624     * TODO(husky): Remove when initialize() is refactored (see TODO there)
625     * TODO(dtrainor) This is still used by other classes.  Make sure to pull some version of this
626     * out before removing it.
627     */
628    public static boolean hasHardwareAcceleration(Activity activity) {
629        // Has HW acceleration been enabled manually in the current window?
630        Window window = activity.getWindow();
631        if (window != null) {
632            if ((window.getAttributes().flags
633                    & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) {
634                return true;
635            }
636        }
637
638        // Has HW acceleration been enabled in the manifest?
639        try {
640            ActivityInfo info = activity.getPackageManager().getActivityInfo(
641                    activity.getComponentName(), 0);
642            if ((info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
643                return true;
644            }
645        } catch (PackageManager.NameNotFoundException e) {
646            Log.e("Chrome", "getActivityInfo(self) should not fail");
647        }
648
649        return false;
650    }
651
652    /**
653     * Returns true if the given Context is a HW-accelerated Activity.
654     *
655     * TODO(husky): Remove when initialize() is refactored (see TODO there)
656     */
657    private static boolean hasHardwareAcceleration(Context context) {
658        if (context instanceof Activity) {
659            return hasHardwareAcceleration((Activity) context);
660        }
661        return false;
662    }
663
664    /**
665     *
666     * @param containerView The view that will act as a container for all views created by this.
667     * @param internalDispatcher Handles dispatching all hidden or super methods to the
668     *                           containerView.
669     * @param nativeWebContents A pointer to the native web contents.
670     * @param windowAndroid An instance of the WindowAndroid.
671     */
672    // Perform important post-construction set up of the ContentViewCore.
673    // We do not require the containing view in the constructor to allow embedders to create a
674    // ContentViewCore without having fully created its containing view. The containing view
675    // is a vital component of the ContentViewCore, so embedders must exercise caution in what
676    // they do with the ContentViewCore before calling initialize().
677    // We supply the nativeWebContents pointer here rather than in the constructor to allow us
678    // to set the private browsing mode at a later point for the WebView implementation.
679    // Note that the caller remains the owner of the nativeWebContents and is responsible for
680    // deleting it after destroying the ContentViewCore.
681    public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
682            int nativeWebContents, WindowAndroid windowAndroid,
683            int inputEventDeliveryMode) {
684        // Check whether to use hardware acceleration. This is a bit hacky, and
685        // only works if the Context is actually an Activity (as it is in the
686        // Chrome application).
687        //
688        // What we're doing here is checking whether the app has *requested*
689        // hardware acceleration by setting the appropriate flags. This does not
690        // necessarily mean we're going to *get* hardware acceleration -- that's
691        // up to the Android framework.
692        //
693        // TODO(husky): Once the native code has been updated so that the
694        // HW acceleration flag can be set dynamically (Grace is doing this),
695        // move this check into onAttachedToWindow(), where we can test for
696        // HW support directly.
697        mHardwareAccelerated = hasHardwareAcceleration(mContext);
698
699        mContainerView = containerView;
700
701        int windowNativePointer = windowAndroid != null ? windowAndroid.getNativePointer() : 0;
702
703        int viewAndroidNativePointer = 0;
704        if (windowNativePointer != 0) {
705            mViewAndroid = new ViewAndroid(windowAndroid, getViewAndroidDelegate());
706            viewAndroidNativePointer = mViewAndroid.getNativePointer();
707        }
708
709        mNativeContentViewCore = nativeInit(mHardwareAccelerated,
710                nativeWebContents, viewAndroidNativePointer, windowNativePointer);
711        mContentSettings = new ContentSettings(this, mNativeContentViewCore);
712        initializeContainerView(internalDispatcher, inputEventDeliveryMode);
713
714        mAccessibilityInjector = AccessibilityInjector.newInstance(this);
715
716        String contentDescription = "Web View";
717        if (R.string.accessibility_content_view == 0) {
718            Log.w(TAG, "Setting contentDescription to 'Web View' as no value was specified.");
719        } else {
720            contentDescription = mContext.getResources().getString(
721                    R.string.accessibility_content_view);
722        }
723        mContainerView.setContentDescription(contentDescription);
724        mWebContentsObserver = new WebContentsObserverAndroid(this) {
725            @Override
726            public void didStartLoading(String url) {
727                hidePopupDialog();
728                resetGestureDetectors();
729            }
730        };
731
732        mPid = nativeGetCurrentRenderProcessId(mNativeContentViewCore);
733    }
734
735    @CalledByNative
736    void onNativeContentViewCoreDestroyed(int nativeContentViewCore) {
737        assert nativeContentViewCore == mNativeContentViewCore;
738        mNativeContentViewCore = 0;
739    }
740
741    /**
742     * Initializes the View that will contain all Views created by the ContentViewCore.
743     *
744     * @param internalDispatcher Handles dispatching all hidden or super methods to the
745     *                           containerView.
746     */
747    private void initializeContainerView(InternalAccessDelegate internalDispatcher,
748            int inputEventDeliveryMode) {
749        TraceEvent.begin();
750        mContainerViewInternals = internalDispatcher;
751
752        mContainerView.setWillNotDraw(false);
753        mContainerView.setFocusable(true);
754        mContainerView.setFocusableInTouchMode(true);
755        mContainerView.setClickable(true);
756
757        if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
758            mContainerView.setHorizontalScrollBarEnabled(false);
759            mContainerView.setVerticalScrollBarEnabled(false);
760        }
761
762        mZoomManager = new ZoomManager(mContext, this);
763        mContentViewGestureHandler = new ContentViewGestureHandler(mContext, this, mZoomManager,
764                inputEventDeliveryMode);
765        mZoomControlsDelegate = new ZoomControlsDelegate() {
766            @Override
767            public void invokeZoomPicker() {}
768            @Override
769            public void dismissZoomPicker() {}
770            @Override
771            public void updateZoomControls() {}
772        };
773
774        mRenderCoordinates.reset();
775
776        initPopupZoomer(mContext);
777        mImeAdapter = createImeAdapter(mContext);
778        TraceEvent.end();
779    }
780
781    private void initPopupZoomer(Context context){
782        mPopupZoomer = new PopupZoomer(context);
783        mPopupZoomer.setOnVisibilityChangedListener(new PopupZoomer.OnVisibilityChangedListener() {
784            @Override
785            public void onPopupZoomerShown(final PopupZoomer zoomer) {
786                mContainerView.post(new Runnable() {
787                    @Override
788                    public void run() {
789                        if (mContainerView.indexOfChild(zoomer) == -1) {
790                            mContainerView.addView(zoomer);
791                        } else {
792                            assert false : "PopupZoomer should never be shown without being hidden";
793                        }
794                    }
795                });
796            }
797
798            @Override
799            public void onPopupZoomerHidden(final PopupZoomer zoomer) {
800                mContainerView.post(new Runnable() {
801                    @Override
802                    public void run() {
803                        if (mContainerView.indexOfChild(zoomer) != -1) {
804                            mContainerView.removeView(zoomer);
805                            mContainerView.invalidate();
806                        } else {
807                            assert false : "PopupZoomer should never be hidden without being shown";
808                        }
809                    }
810                });
811            }
812        });
813        // TODO(yongsheng): LONG_TAP is not enabled in PopupZoomer. So need to dispatch a LONG_TAP
814        // gesture if a user completes a tap on PopupZoomer UI after a LONG_PRESS gesture.
815        PopupZoomer.OnTapListener listener = new PopupZoomer.OnTapListener() {
816            @Override
817            public boolean onSingleTap(View v, MotionEvent e) {
818                mContainerView.requestFocus();
819                if (mNativeContentViewCore != 0) {
820                    nativeSingleTap(mNativeContentViewCore, e.getEventTime(),
821                            e.getX(), e.getY(), true);
822                }
823                return true;
824            }
825
826            @Override
827            public boolean onLongPress(View v, MotionEvent e) {
828                if (mNativeContentViewCore != 0) {
829                    nativeLongPress(mNativeContentViewCore, e.getEventTime(),
830                            e.getX(), e.getY(), true);
831                }
832                return true;
833            }
834        };
835        mPopupZoomer.setOnTapListener(listener);
836    }
837
838    /**
839     * Destroy the internal state of the ContentView. This method may only be
840     * called after the ContentView has been removed from the view system. No
841     * other methods may be called on this ContentView after this method has
842     * been called.
843     */
844    public void destroy() {
845        if (mNativeContentViewCore != 0) {
846            nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore);
847        }
848        resetVSyncNotification();
849        mVSyncProvider = null;
850        if (mViewAndroid != null) mViewAndroid.destroy();
851        mNativeContentViewCore = 0;
852        mContentSettings = null;
853        mJavaScriptInterfaces.clear();
854        mRetainedJavaScriptObjects.clear();
855        unregisterAccessibilityContentObserver();
856    }
857
858    private void unregisterAccessibilityContentObserver() {
859        if (mAccessibilityScriptInjectionObserver == null) {
860            return;
861        }
862        getContext().getContentResolver().unregisterContentObserver(
863                mAccessibilityScriptInjectionObserver);
864        mAccessibilityScriptInjectionObserver = null;
865    }
866
867    /**
868     * Returns true initially, false after destroy() has been called.
869     * It is illegal to call any other public method after destroy().
870     */
871    public boolean isAlive() {
872        return mNativeContentViewCore != 0;
873    }
874
875    /**
876     * This is only useful for passing over JNI to native code that requires ContentViewCore*.
877     * @return native ContentViewCore pointer.
878     */
879    @CalledByNative
880    public int getNativeContentViewCore() {
881        return mNativeContentViewCore;
882    }
883
884    /**
885     * For internal use. Throws IllegalStateException if mNativeContentView is 0.
886     * Use this to ensure we get a useful Java stack trace, rather than a native
887     * crash dump, from use-after-destroy bugs in Java code.
888     */
889    void checkIsAlive() throws IllegalStateException {
890        if (!isAlive()) {
891            throw new IllegalStateException("ContentView used after destroy() was called");
892        }
893    }
894
895    public void setContentViewClient(ContentViewClient client) {
896        if (client == null) {
897            throw new IllegalArgumentException("The client can't be null.");
898        }
899        mContentViewClient = client;
900    }
901
902    ContentViewClient getContentViewClient() {
903        if (mContentViewClient == null) {
904            // We use the Null Object pattern to avoid having to perform a null check in this class.
905            // We create it lazily because most of the time a client will be set almost immediately
906            // after ContentView is created.
907            mContentViewClient = new ContentViewClient();
908            // We don't set the native ContentViewClient pointer here on purpose. The native
909            // implementation doesn't mind a null delegate and using one is better than passing a
910            // Null Object, since we cut down on the number of JNI calls.
911        }
912        return mContentViewClient;
913    }
914
915    public int getBackgroundColor() {
916        if (mNativeContentViewCore != 0) {
917            return nativeGetBackgroundColor(mNativeContentViewCore);
918        }
919        return Color.WHITE;
920    }
921
922    @CalledByNative
923    private void onBackgroundColorChanged(int color) {
924        getContentViewClient().onBackgroundColorChanged(color);
925    }
926
927    /**
928     * Load url without fixing up the url string. Consumers of ContentView are responsible for
929     * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
930     * off during user input).
931     *
932     * @param params Parameters for this load.
933     */
934    public void loadUrl(LoadUrlParams params) {
935        if (mNativeContentViewCore == 0) return;
936
937        nativeLoadUrl(mNativeContentViewCore,
938                params.mUrl,
939                params.mLoadUrlType,
940                params.mTransitionType,
941                params.mUaOverrideOption,
942                params.getExtraHeadersString(),
943                params.mPostData,
944                params.mBaseUrlForDataUrl,
945                params.mVirtualUrlForDataUrl,
946                params.mCanLoadLocalResources);
947    }
948
949    /**
950     * Stops loading the current web contents.
951     */
952    public void stopLoading() {
953        if (mNativeContentViewCore != 0) nativeStopLoading(mNativeContentViewCore);
954    }
955
956    /**
957     * Get the URL of the current page.
958     *
959     * @return The URL of the current page.
960     */
961    public String getUrl() {
962        if (mNativeContentViewCore != 0) return nativeGetURL(mNativeContentViewCore);
963        return null;
964    }
965
966    /**
967     * Get the title of the current page.
968     *
969     * @return The title of the current page.
970     */
971    public String getTitle() {
972        if (mNativeContentViewCore != 0) return nativeGetTitle(mNativeContentViewCore);
973        return null;
974    }
975
976    /**
977     * Shows an interstitial page driven by the passed in delegate.
978     *
979     * @param url The URL being blocked by the interstitial.
980     * @param delegate The delegate handling the interstitial.
981     */
982    @VisibleForTesting
983    public void showInterstitialPage(
984            String url, InterstitialPageDelegateAndroid delegate) {
985        if (mNativeContentViewCore == 0) return;
986        nativeShowInterstitialPage(mNativeContentViewCore, url, delegate.getNative());
987    }
988
989    /**
990     * @return Whether the page is currently showing an interstitial, such as a bad HTTPS page.
991     */
992    public boolean isShowingInterstitialPage() {
993        return mNativeContentViewCore == 0 ?
994                false : nativeIsShowingInterstitialPage(mNativeContentViewCore);
995    }
996
997    /**
998     * Mark any new frames that have arrived since this function was last called as non-pending.
999     *
1000     * @return Whether there was a pending frame from the renderer.
1001     */
1002    public boolean consumePendingRendererFrame() {
1003        boolean hadPendingFrame = mPendingRendererFrame;
1004        mPendingRendererFrame = false;
1005        return hadPendingFrame;
1006    }
1007
1008    /**
1009     * @return Viewport width in physical pixels as set from onSizeChanged.
1010     */
1011    @CalledByNative
1012    public int getViewportWidthPix() { return mViewportWidthPix; }
1013
1014    /**
1015     * @return Viewport height in physical pixels as set from onSizeChanged.
1016     */
1017    @CalledByNative
1018    public int getViewportHeightPix() { return mViewportHeightPix; }
1019
1020    /**
1021     * @return Width of underlying physical surface.
1022     */
1023    @CalledByNative
1024    public int getPhysicalBackingWidthPix() { return mPhysicalBackingWidthPix; }
1025
1026    /**
1027     * @return Height of underlying physical surface.
1028     */
1029    @CalledByNative
1030    public int getPhysicalBackingHeightPix() { return mPhysicalBackingHeightPix; }
1031
1032    /**
1033     * @return Amount the output surface extends past the bottom of the window viewport.
1034     */
1035    @CalledByNative
1036    public int getOverdrawBottomHeightPix() { return mOverdrawBottomHeightPix; }
1037
1038    /**
1039     * @return The amount to shrink the viewport relative to {@link #getViewportWidthPix()}.
1040     */
1041    @CalledByNative
1042    public int getViewportSizeOffsetWidthPix() { return mViewportSizeOffsetWidthPix; }
1043
1044    /**
1045     * @return The amount to shrink the viewport relative to {@link #getViewportHeightPix()}.
1046     */
1047    @CalledByNative
1048    public int getViewportSizeOffsetHeightPix() { return mViewportSizeOffsetHeightPix; }
1049
1050    /**
1051     * @see android.webkit.WebView#getContentHeight()
1052     */
1053    public float getContentHeightCss() {
1054        return mRenderCoordinates.getContentHeightCss();
1055    }
1056
1057    /**
1058     * @see android.webkit.WebView#getContentWidth()
1059     */
1060    public float getContentWidthCss() {
1061        return mRenderCoordinates.getContentWidthCss();
1062    }
1063
1064    public Bitmap getBitmap() {
1065        return getBitmap(getViewportWidthPix(), getViewportHeightPix());
1066    }
1067
1068    public Bitmap getBitmap(int width, int height) {
1069        if (width == 0 || height == 0
1070                || getViewportWidthPix() == 0 || getViewportHeightPix() == 0) {
1071            return null;
1072        }
1073
1074        Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
1075
1076        if (mNativeContentViewCore != 0 &&
1077                nativePopulateBitmapFromCompositor(mNativeContentViewCore, b)) {
1078            // If we successfully grabbed a bitmap, check if we have to draw the Android overlay
1079            // components as well.
1080            if (mContainerView.getChildCount() > 0) {
1081                Canvas c = new Canvas(b);
1082                c.scale(width / (float) getViewportWidthPix(),
1083                        height / (float) getViewportHeightPix());
1084                mContainerView.draw(c);
1085            }
1086            return b;
1087        }
1088
1089        return null;
1090    }
1091
1092    /**
1093     * Generates a bitmap of the content that is performance optimized based on capture time.
1094     *
1095     * <p>
1096     * To have a consistent capture time across devices, we will scale down the captured bitmap
1097     * where necessary to reduce the time to generate the bitmap.
1098     *
1099     * @param width The width of the content to be captured.
1100     * @param height The height of the content to be captured.
1101     * @return A pair of the generated bitmap, and the scale that needs to be applied to return the
1102     *         bitmap to it's original size (i.e. if the bitmap is scaled down 50%, this
1103     *         will be 2).
1104     */
1105    public Pair<Bitmap, Float> getScaledPerformanceOptimizedBitmap(int width, int height) {
1106        float scale = 1f;
1107        // On tablets, always scale down to MDPI for performance reasons.
1108        if (DeviceUtils.isTablet(getContext())) {
1109            scale = getContext().getResources().getDisplayMetrics().density;
1110        }
1111        return Pair.create(
1112                getBitmap((int) (width / scale), (int) (height / scale)),
1113                scale);
1114    }
1115
1116    /**
1117     * @return Whether the current WebContents has a previous navigation entry.
1118     */
1119    public boolean canGoBack() {
1120        return mNativeContentViewCore != 0 && nativeCanGoBack(mNativeContentViewCore);
1121    }
1122
1123    /**
1124     * @return Whether the current WebContents has a navigation entry after the current one.
1125     */
1126    public boolean canGoForward() {
1127        return mNativeContentViewCore != 0 && nativeCanGoForward(mNativeContentViewCore);
1128    }
1129
1130    /**
1131     * @param offset The offset into the navigation history.
1132     * @return Whether we can move in history by given offset
1133     */
1134    public boolean canGoToOffset(int offset) {
1135        return mNativeContentViewCore != 0 && nativeCanGoToOffset(mNativeContentViewCore, offset);
1136    }
1137
1138    /**
1139     * Navigates to the specified offset from the "current entry". Does nothing if the offset is out
1140     * of bounds.
1141     * @param offset The offset into the navigation history.
1142     */
1143    public void goToOffset(int offset) {
1144        if (mNativeContentViewCore != 0) nativeGoToOffset(mNativeContentViewCore, offset);
1145    }
1146
1147    @Override
1148    public void goToNavigationIndex(int index) {
1149        if (mNativeContentViewCore != 0) nativeGoToNavigationIndex(mNativeContentViewCore, index);
1150    }
1151
1152    /**
1153     * Goes to the navigation entry before the current one.
1154     */
1155    public void goBack() {
1156        if (mNativeContentViewCore != 0) nativeGoBack(mNativeContentViewCore);
1157    }
1158
1159    /**
1160     * Goes to the navigation entry following the current one.
1161     */
1162    public void goForward() {
1163        if (mNativeContentViewCore != 0) nativeGoForward(mNativeContentViewCore);
1164    }
1165
1166    /**
1167     * Reload the current page.
1168     */
1169    public void reload() {
1170        mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
1171        if (mNativeContentViewCore != 0) nativeReload(mNativeContentViewCore);
1172    }
1173
1174    /**
1175     * Cancel the pending reload.
1176     */
1177    public void cancelPendingReload() {
1178        if (mNativeContentViewCore != 0) nativeCancelPendingReload(mNativeContentViewCore);
1179    }
1180
1181    /**
1182     * Continue the pending reload.
1183     */
1184    public void continuePendingReload() {
1185        if (mNativeContentViewCore != 0) nativeContinuePendingReload(mNativeContentViewCore);
1186    }
1187
1188    /**
1189     * Clears the ContentViewCore's page history in both the backwards and
1190     * forwards directions.
1191     */
1192    public void clearHistory() {
1193        if (mNativeContentViewCore != 0) nativeClearHistory(mNativeContentViewCore);
1194    }
1195
1196    String getSelectedText() {
1197        return mHasSelection ? mLastSelectedText : "";
1198    }
1199
1200    // End FrameLayout overrides.
1201
1202    /**
1203     * @see {@link android.webkit.WebView#flingScroll(int, int)}
1204     */
1205    public void flingScroll(int vx, int vy) {
1206        // Notes:
1207        //   (1) Use large negative values for the x/y parameters so we don't accidentally scroll a
1208        //       nested frame.
1209        //   (2) vx and vy are inverted to match WebView behavior.
1210        mContentViewGestureHandler.fling(
1211                System.currentTimeMillis(), -Integer.MAX_VALUE, -Integer.MIN_VALUE, -vx, -vy);
1212    }
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    private void resetGestureDetectors() {
1451        mContentViewGestureHandler.resetGestureHandlers();
1452    }
1453
1454    /**
1455     * @see View#onAttachedToWindow()
1456     */
1457    @SuppressWarnings("javadoc")
1458    public void onAttachedToWindow() {
1459        mAttachedToWindow = true;
1460        if (mNativeContentViewCore != 0) {
1461            assert mPid == nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1462            ChildProcessLauncher.bindAsHighPriority(mPid);
1463            // Normally the initial binding is removed in onRenderProcessSwap(), but it is possible
1464            // to construct WebContents and spawn the renderer before passing it to ContentViewCore.
1465            // In this case there will be no onRenderProcessSwap() call and the initial binding will
1466            // be removed here.
1467            ChildProcessLauncher.removeInitialBinding(mPid);
1468        }
1469        setAccessibilityState(mAccessibilityManager.isEnabled());
1470    }
1471
1472    /**
1473     * @see View#onDetachedFromWindow()
1474     */
1475    @SuppressWarnings("javadoc")
1476    public void onDetachedFromWindow() {
1477        mAttachedToWindow = false;
1478        if (mNativeContentViewCore != 0) {
1479            assert mPid == nativeGetCurrentRenderProcessId(mNativeContentViewCore);
1480            ChildProcessLauncher.unbindAsHighPriority(mPid);
1481        }
1482        setInjectedAccessibility(false);
1483        hidePopupDialog();
1484        mZoomControlsDelegate.dismissZoomPicker();
1485        unregisterAccessibilityContentObserver();
1486    }
1487
1488    /**
1489     * @see View#onVisibilityChanged(android.view.View, int)
1490     */
1491    public void onVisibilityChanged(View changedView, int visibility) {
1492        if (visibility != View.VISIBLE) {
1493            mZoomControlsDelegate.dismissZoomPicker();
1494        }
1495    }
1496
1497    /**
1498     * @see View#onCreateInputConnection(EditorInfo)
1499     */
1500    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1501        if (!mImeAdapter.hasTextInputType()) {
1502            // Although onCheckIsTextEditor will return false in this case, the EditorInfo
1503            // is still used by the InputMethodService. Need to make sure the IME doesn't
1504            // enter fullscreen mode.
1505            outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
1506        }
1507        mInputConnection =
1508                mAdapterInputConnectionFactory.get(mContainerView, mImeAdapter, outAttrs);
1509        return mInputConnection;
1510    }
1511
1512    public Editable getEditableForTest() {
1513        return mInputConnection.getEditable();
1514    }
1515
1516    /**
1517     * @see View#onCheckIsTextEditor()
1518     */
1519    public boolean onCheckIsTextEditor() {
1520        return mImeAdapter.hasTextInputType();
1521    }
1522
1523    /**
1524     * @see View#onConfigurationChanged(Configuration)
1525     */
1526    @SuppressWarnings("javadoc")
1527    public void onConfigurationChanged(Configuration newConfig) {
1528        TraceEvent.begin();
1529
1530        if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
1531            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
1532                    ImeAdapter.getTextInputTypeNone(),
1533                    AdapterInputConnection.INVALID_SELECTION,
1534                    AdapterInputConnection.INVALID_SELECTION);
1535            InputMethodManager manager = (InputMethodManager)
1536                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
1537            manager.restartInput(mContainerView);
1538        }
1539        mContainerViewInternals.super_onConfigurationChanged(newConfig);
1540        mNeedUpdateOrientationChanged = true;
1541        TraceEvent.end();
1542    }
1543
1544    /**
1545     * @see View#onSizeChanged(int, int, int, int)
1546     */
1547    @SuppressWarnings("javadoc")
1548    public void onSizeChanged(int wPix, int hPix, int owPix, int ohPix) {
1549        if (getViewportWidthPix() == wPix && getViewportHeightPix() == hPix) return;
1550
1551        mViewportWidthPix = wPix;
1552        mViewportHeightPix = hPix;
1553        if (mNativeContentViewCore != 0) {
1554            nativeWasResized(mNativeContentViewCore);
1555        }
1556
1557        updateAfterSizeChanged();
1558    }
1559
1560    /**
1561     * Called when the underlying surface the compositor draws to changes size.
1562     * This may be larger than the viewport size.
1563     */
1564    public void onPhysicalBackingSizeChanged(int wPix, int hPix) {
1565        if (mPhysicalBackingWidthPix == wPix && mPhysicalBackingHeightPix == hPix) return;
1566
1567        mPhysicalBackingWidthPix = wPix;
1568        mPhysicalBackingHeightPix = hPix;
1569
1570        if (mNativeContentViewCore != 0) {
1571            nativeWasResized(mNativeContentViewCore);
1572        }
1573    }
1574
1575    /**
1576     * Called when the amount the surface is overdrawing off the bottom has changed.
1577     * @param overdrawHeightPix The overdraw height.
1578     */
1579    public void onOverdrawBottomHeightChanged(int overdrawHeightPix) {
1580        if (mOverdrawBottomHeightPix == overdrawHeightPix) return;
1581
1582        mOverdrawBottomHeightPix = overdrawHeightPix;
1583
1584        if (mNativeContentViewCore != 0) {
1585            nativeWasResized(mNativeContentViewCore);
1586        }
1587    }
1588
1589    private void updateAfterSizeChanged() {
1590        mPopupZoomer.hide(false);
1591
1592        // Execute a delayed form focus operation because the OSK was brought
1593        // up earlier.
1594        if (!mFocusPreOSKViewportRect.isEmpty()) {
1595            Rect rect = new Rect();
1596            getContainerView().getWindowVisibleDisplayFrame(rect);
1597            if (!rect.equals(mFocusPreOSKViewportRect)) {
1598                scrollFocusedEditableNodeIntoView();
1599                mFocusPreOSKViewportRect.setEmpty();
1600            }
1601        } else if (mUnfocusOnNextSizeChanged) {
1602            undoScrollFocusedEditableNodeIntoViewIfNeeded(true);
1603            mUnfocusOnNextSizeChanged = false;
1604        }
1605
1606        if (mNeedUpdateOrientationChanged) {
1607            sendOrientationChangeEvent();
1608            mNeedUpdateOrientationChanged = false;
1609        }
1610    }
1611
1612    private void scrollFocusedEditableNodeIntoView() {
1613        if (mNativeContentViewCore != 0) {
1614            Runnable scrollTask = new Runnable() {
1615                @Override
1616                public void run() {
1617                    if (mNativeContentViewCore != 0) {
1618                        nativeScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1619                    }
1620                }
1621            };
1622
1623            scrollTask.run();
1624
1625            // The native side keeps track of whether the zoom and scroll actually occurred. It is
1626            // more efficient to do it this way and sometimes fire an unnecessary message rather
1627            // than synchronize with the renderer and always have an additional message.
1628            mScrolledAndZoomedFocusedEditableNode = true;
1629        }
1630    }
1631
1632    private void undoScrollFocusedEditableNodeIntoViewIfNeeded(boolean backButtonPressed) {
1633        // The only call to this function that matters is the first call after the
1634        // scrollFocusedEditableNodeIntoView function call.
1635        // If the first call to this function is a result of a back button press we want to undo the
1636        // preceding scroll. If the call is a result of some other action we don't want to perform
1637        // an undo.
1638        // All subsequent calls are ignored since only the scroll function sets
1639        // mScrolledAndZoomedFocusedEditableNode to true.
1640        if (mScrolledAndZoomedFocusedEditableNode && backButtonPressed &&
1641                mNativeContentViewCore != 0) {
1642            Runnable scrollTask = new Runnable() {
1643                @Override
1644                public void run() {
1645                    if (mNativeContentViewCore != 0) {
1646                        nativeUndoScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1647                    }
1648                }
1649            };
1650
1651            scrollTask.run();
1652        }
1653        mScrolledAndZoomedFocusedEditableNode = false;
1654    }
1655
1656    public void onFocusChanged(boolean gainFocus) {
1657        if (!gainFocus) getContentViewClient().onImeStateChangeRequested(false);
1658        if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1659    }
1660
1661    /**
1662     * @see View#onKeyUp(int, KeyEvent)
1663     */
1664    public boolean onKeyUp(int keyCode, KeyEvent event) {
1665        if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1666            mPopupZoomer.hide(true);
1667            return true;
1668        }
1669        return mContainerViewInternals.super_onKeyUp(keyCode, event);
1670    }
1671
1672    /**
1673     * @see View#dispatchKeyEventPreIme(KeyEvent)
1674     */
1675    public boolean dispatchKeyEventPreIme(KeyEvent event) {
1676        try {
1677            TraceEvent.begin();
1678            if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && mImeAdapter.isActive()) {
1679                mUnfocusOnNextSizeChanged = true;
1680            } else {
1681                undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
1682            }
1683            return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1684        } finally {
1685            TraceEvent.end();
1686        }
1687    }
1688
1689    /**
1690     * @see View#dispatchKeyEvent(KeyEvent)
1691     */
1692    public boolean dispatchKeyEvent(KeyEvent event) {
1693        if (getContentViewClient().shouldOverrideKeyEvent(event)) {
1694            return mContainerViewInternals.super_dispatchKeyEvent(event);
1695        }
1696
1697        if (mImeAdapter.dispatchKeyEvent(event)) return true;
1698
1699        return mContainerViewInternals.super_dispatchKeyEvent(event);
1700    }
1701
1702    /**
1703     * @see View#onHoverEvent(MotionEvent)
1704     * Mouse move events are sent on hover enter, hover move and hover exit.
1705     * They are sent on hover exit because sometimes it acts as both a hover
1706     * move and hover exit.
1707     */
1708    public boolean onHoverEvent(MotionEvent event) {
1709        TraceEvent.begin("onHoverEvent");
1710        mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1711        if (mBrowserAccessibilityManager != null) {
1712            return mBrowserAccessibilityManager.onHoverEvent(event);
1713        }
1714        if (mNativeContentViewCore != 0) {
1715            nativeSendMouseMoveEvent(mNativeContentViewCore, event.getEventTime(),
1716                    event.getX(), event.getY());
1717        }
1718        TraceEvent.end("onHoverEvent");
1719        return true;
1720    }
1721
1722    /**
1723     * @see View#onGenericMotionEvent(MotionEvent)
1724     */
1725    public boolean onGenericMotionEvent(MotionEvent event) {
1726        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1727            switch (event.getAction()) {
1728                case MotionEvent.ACTION_SCROLL:
1729                    nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
1730                            event.getX(), event.getY(),
1731                            event.getAxisValue(MotionEvent.AXIS_VSCROLL));
1732
1733                    mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1734                    // Send a delayed onMouseMove event so that we end
1735                    // up hovering over the right position after the scroll.
1736                    final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event);
1737                    mFakeMouseMoveRunnable = new Runnable() {
1738                          @Override
1739                          public void run() {
1740                              onHoverEvent(eventFakeMouseMove);
1741                          }
1742                    };
1743                    mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1744                    return true;
1745            }
1746        }
1747        return mContainerViewInternals.super_onGenericMotionEvent(event);
1748    }
1749
1750    /**
1751     * @see View#scrollBy(int, int)
1752     * Currently the ContentView scrolling happens in the native side. In
1753     * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
1754     * are overridden, so that View's mScrollX and mScrollY will be unchanged at
1755     * (0, 0). This is critical for drawing ContentView correctly.
1756     */
1757    public void scrollBy(int xPix, int yPix) {
1758        if (mNativeContentViewCore != 0) {
1759            nativeScrollBy(mNativeContentViewCore,
1760                    System.currentTimeMillis(), 0, 0, xPix, yPix, false);
1761        }
1762    }
1763
1764    /**
1765     * @see View#scrollTo(int, int)
1766     */
1767    public void scrollTo(int xPix, int yPix) {
1768        if (mNativeContentViewCore == 0) return;
1769        final float xCurrentPix = mRenderCoordinates.getScrollXPix();
1770        final float yCurrentPix = mRenderCoordinates.getScrollYPix();
1771        final float dxPix = xPix - xCurrentPix;
1772        final float dyPix = yPix - yCurrentPix;
1773        if (dxPix != 0 || dyPix != 0) {
1774            long time = System.currentTimeMillis();
1775            nativeScrollBegin(mNativeContentViewCore, time, xCurrentPix, yCurrentPix);
1776            nativeScrollBy(mNativeContentViewCore,
1777                    time, xCurrentPix, yCurrentPix, dxPix, dyPix, false);
1778            nativeScrollEnd(mNativeContentViewCore, time);
1779        }
1780    }
1781
1782    // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1783    //       see: b/6029133
1784    public int getNativeScrollXForTest() {
1785        return mRenderCoordinates.getScrollXPixInt();
1786    }
1787
1788    // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1789    //       see: b/6029133
1790    public int getNativeScrollYForTest() {
1791        return mRenderCoordinates.getScrollYPixInt();
1792    }
1793
1794    /**
1795     * @see View#computeHorizontalScrollExtent()
1796     */
1797    @SuppressWarnings("javadoc")
1798    public int computeHorizontalScrollExtent() {
1799        return mRenderCoordinates.getLastFrameViewportWidthPixInt();
1800    }
1801
1802    /**
1803     * @see View#computeHorizontalScrollOffset()
1804     */
1805    @SuppressWarnings("javadoc")
1806    public int computeHorizontalScrollOffset() {
1807        return mRenderCoordinates.getScrollXPixInt();
1808    }
1809
1810    /**
1811     * @see View#computeHorizontalScrollRange()
1812     */
1813    @SuppressWarnings("javadoc")
1814    public int computeHorizontalScrollRange() {
1815        return mRenderCoordinates.getContentWidthPixInt();
1816    }
1817
1818    /**
1819     * @see View#computeVerticalScrollExtent()
1820     */
1821    @SuppressWarnings("javadoc")
1822    public int computeVerticalScrollExtent() {
1823        return mRenderCoordinates.getLastFrameViewportHeightPixInt();
1824    }
1825
1826    /**
1827     * @see View#computeVerticalScrollOffset()
1828     */
1829    @SuppressWarnings("javadoc")
1830    public int computeVerticalScrollOffset() {
1831        return mRenderCoordinates.getScrollYPixInt();
1832    }
1833
1834    /**
1835     * @see View#computeVerticalScrollRange()
1836     */
1837    @SuppressWarnings("javadoc")
1838    public int computeVerticalScrollRange() {
1839        return mRenderCoordinates.getContentHeightPixInt();
1840    }
1841
1842    // End FrameLayout overrides.
1843
1844    /**
1845     * @see View#awakenScrollBars(int, boolean)
1846     */
1847    @SuppressWarnings("javadoc")
1848    public boolean awakenScrollBars(int startDelay, boolean invalidate) {
1849        // For the default implementation of ContentView which draws the scrollBars on the native
1850        // side, calling this function may get us into a bad state where we keep drawing the
1851        // scrollBars, so disable it by always returning false.
1852        if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
1853            return false;
1854        } else {
1855            return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
1856        }
1857    }
1858
1859    @SuppressWarnings("unused")
1860    @CalledByNative
1861    private void onTabCrash() {
1862        assert mPid != 0;
1863        getContentViewClient().onRendererCrash(ChildProcessLauncher.isOomProtected(mPid));
1864        mPid = 0;
1865    }
1866
1867    private void handleTapOrPress(
1868            long timeMs, float xPix, float yPix, int isLongPressOrTap, boolean showPress) {
1869        if (!mContainerView.isFocused()) mContainerView.requestFocus();
1870
1871        if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
1872
1873        if (isLongPressOrTap == IS_LONG_PRESS) {
1874            getInsertionHandleController().allowAutomaticShowing();
1875            getSelectionHandleController().allowAutomaticShowing();
1876            if (mNativeContentViewCore != 0) {
1877                nativeLongPress(mNativeContentViewCore, timeMs, xPix, yPix, false);
1878            }
1879        } else if (isLongPressOrTap == IS_LONG_TAP) {
1880            getInsertionHandleController().allowAutomaticShowing();
1881            getSelectionHandleController().allowAutomaticShowing();
1882            if (mNativeContentViewCore != 0) {
1883                nativeLongTap(mNativeContentViewCore, timeMs, xPix, yPix, false);
1884            }
1885        } else {
1886            if (!showPress && mNativeContentViewCore != 0) {
1887                nativeShowPressState(mNativeContentViewCore, timeMs, xPix, yPix);
1888            }
1889            if (mSelectionEditable) getInsertionHandleController().allowAutomaticShowing();
1890            if (mNativeContentViewCore != 0) {
1891                nativeSingleTap(mNativeContentViewCore, timeMs, xPix, yPix, false);
1892            }
1893        }
1894    }
1895
1896    public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
1897        mZoomControlsDelegate = zoomControlsDelegate;
1898    }
1899
1900    public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
1901        mZoomManager.updateMultiTouchSupport(supportsMultiTouchZoom);
1902    }
1903
1904    public void selectPopupMenuItems(int[] indices) {
1905        if (mNativeContentViewCore != 0) {
1906            nativeSelectPopupMenuItems(mNativeContentViewCore, indices);
1907        }
1908    }
1909
1910    /**
1911     * Get the screen orientation from the OS and push it to WebKit.
1912     *
1913     * TODO(husky): Add a hook for mock orientations.
1914     *
1915     * TODO(husky): Currently each new tab starts with an orientation of 0 until you actually
1916     * rotate the device. This is wrong if you actually started in landscape mode. To fix this, we
1917     * need to push the correct orientation, but only after WebKit's Frame object has been fully
1918     * initialized. Need to find a good time to do that. onPageFinished() would probably work but
1919     * it isn't implemented yet.
1920     */
1921    private void sendOrientationChangeEvent() {
1922        if (mNativeContentViewCore == 0) return;
1923
1924        WindowManager windowManager =
1925                (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
1926        switch (windowManager.getDefaultDisplay().getRotation()) {
1927            case Surface.ROTATION_90:
1928                nativeSendOrientationChangeEvent(mNativeContentViewCore, 90);
1929                break;
1930            case Surface.ROTATION_180:
1931                nativeSendOrientationChangeEvent(mNativeContentViewCore, 180);
1932                break;
1933            case Surface.ROTATION_270:
1934                nativeSendOrientationChangeEvent(mNativeContentViewCore, -90);
1935                break;
1936            case Surface.ROTATION_0:
1937                nativeSendOrientationChangeEvent(mNativeContentViewCore, 0);
1938                break;
1939            default:
1940                Log.w(TAG, "Unknown rotation!");
1941                break;
1942        }
1943    }
1944
1945    /**
1946     * Register the delegate to be used when content can not be handled by
1947     * the rendering engine, and should be downloaded instead. This will replace
1948     * the current delegate, if any.
1949     * @param delegate An implementation of ContentViewDownloadDelegate.
1950     */
1951    public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
1952        mDownloadDelegate = delegate;
1953    }
1954
1955    // Called by DownloadController.
1956    ContentViewDownloadDelegate getDownloadDelegate() {
1957        return mDownloadDelegate;
1958    }
1959
1960    private SelectionHandleController getSelectionHandleController() {
1961        if (mSelectionHandleController == null) {
1962            mSelectionHandleController = new SelectionHandleController(getContainerView()) {
1963                @Override
1964                public void selectBetweenCoordinates(int x1, int y1, int x2, int y2) {
1965                    if (mNativeContentViewCore != 0 && !(x1 == x2 && y1 == y2)) {
1966                        nativeSelectBetweenCoordinates(mNativeContentViewCore,
1967                                x1, y1 - mRenderCoordinates.getContentOffsetYPix(),
1968                                x2, y2 - mRenderCoordinates.getContentOffsetYPix());
1969                    }
1970                }
1971
1972                @Override
1973                public void showHandles(int startDir, int endDir) {
1974                    super.showHandles(startDir, endDir);
1975                    showSelectActionBar();
1976                }
1977
1978            };
1979
1980            mSelectionHandleController.hideAndDisallowAutomaticShowing();
1981        }
1982
1983        return mSelectionHandleController;
1984    }
1985
1986    private InsertionHandleController getInsertionHandleController() {
1987        if (mInsertionHandleController == null) {
1988            mInsertionHandleController = new InsertionHandleController(getContainerView()) {
1989                private static final int AVERAGE_LINE_HEIGHT = 14;
1990
1991                @Override
1992                public void setCursorPosition(int x, int y) {
1993                    if (mNativeContentViewCore != 0) {
1994                        nativeMoveCaret(mNativeContentViewCore,
1995                                x, y - mRenderCoordinates.getContentOffsetYPix());
1996                    }
1997                }
1998
1999                @Override
2000                public void paste() {
2001                    mImeAdapter.paste();
2002                    hideHandles();
2003                }
2004
2005                @Override
2006                public int getLineHeight() {
2007                    return (int) Math.ceil(
2008                            mRenderCoordinates.fromLocalCssToPix(AVERAGE_LINE_HEIGHT));
2009                }
2010
2011                @Override
2012                public void showHandle() {
2013                    super.showHandle();
2014                }
2015            };
2016
2017            mInsertionHandleController.hideAndDisallowAutomaticShowing();
2018        }
2019
2020        return mInsertionHandleController;
2021    }
2022
2023    public InsertionHandleController getInsertionHandleControllerForTest() {
2024        return mInsertionHandleController;
2025    }
2026
2027    private void updateHandleScreenPositions() {
2028        if (isSelectionHandleShowing()) {
2029            mSelectionHandleController.setStartHandlePosition(
2030                    mStartHandlePoint.getXPix(), mStartHandlePoint.getYPix());
2031            mSelectionHandleController.setEndHandlePosition(
2032                    mEndHandlePoint.getXPix(), mEndHandlePoint.getYPix());
2033        }
2034
2035        if (isInsertionHandleShowing()) {
2036            mInsertionHandleController.setHandlePosition(
2037                    mInsertionHandlePoint.getXPix(), mInsertionHandlePoint.getYPix());
2038        }
2039    }
2040
2041    private void hideHandles() {
2042        if (mSelectionHandleController != null) {
2043            mSelectionHandleController.hideAndDisallowAutomaticShowing();
2044        }
2045        if (mInsertionHandleController != null) {
2046            mInsertionHandleController.hideAndDisallowAutomaticShowing();
2047        }
2048    }
2049
2050    private void showSelectActionBar() {
2051        if (mActionMode != null) {
2052            mActionMode.invalidate();
2053            return;
2054        }
2055
2056        // Start a new action mode with a SelectActionModeCallback.
2057        SelectActionModeCallback.ActionHandler actionHandler =
2058                new SelectActionModeCallback.ActionHandler() {
2059            @Override
2060            public boolean selectAll() {
2061                return mImeAdapter.selectAll();
2062            }
2063
2064            @Override
2065            public boolean cut() {
2066                return mImeAdapter.cut();
2067            }
2068
2069            @Override
2070            public boolean copy() {
2071                return mImeAdapter.copy();
2072            }
2073
2074            @Override
2075            public boolean paste() {
2076                return mImeAdapter.paste();
2077            }
2078
2079            @Override
2080            public boolean isSelectionEditable() {
2081                return mSelectionEditable;
2082            }
2083
2084            @Override
2085            public String getSelectedText() {
2086                return ContentViewCore.this.getSelectedText();
2087            }
2088
2089            @Override
2090            public void onDestroyActionMode() {
2091                mActionMode = null;
2092                if (mUnselectAllOnActionModeDismiss) mImeAdapter.unselect();
2093                getContentViewClient().onContextualActionBarHidden();
2094            }
2095        };
2096        mActionMode = null;
2097        // On ICS, startActionMode throws an NPE when getParent() is null.
2098        if (mContainerView.getParent() != null) {
2099            mActionMode = mContainerView.startActionMode(
2100                    getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler,
2101                            nativeIsIncognito(mNativeContentViewCore)));
2102        }
2103        mUnselectAllOnActionModeDismiss = true;
2104        if (mActionMode == null) {
2105            // There is no ActionMode, so remove the selection.
2106            mImeAdapter.unselect();
2107        } else {
2108            getContentViewClient().onContextualActionBarShown();
2109        }
2110    }
2111
2112    public boolean getUseDesktopUserAgent() {
2113        if (mNativeContentViewCore != 0) {
2114            return nativeGetUseDesktopUserAgent(mNativeContentViewCore);
2115        }
2116        return false;
2117    }
2118
2119    /**
2120     * Set whether or not we're using a desktop user agent for the currently loaded page.
2121     * @param override If true, use a desktop user agent.  Use a mobile one otherwise.
2122     * @param reloadOnChange Reload the page if the UA has changed.
2123     */
2124    public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange) {
2125        if (mNativeContentViewCore != 0) {
2126            nativeSetUseDesktopUserAgent(mNativeContentViewCore, override, reloadOnChange);
2127        }
2128    }
2129
2130    public void clearSslPreferences() {
2131        nativeClearSslPreferences(mNativeContentViewCore);
2132    }
2133
2134    /**
2135     * @return Whether the native ContentView has crashed.
2136     */
2137    public boolean isCrashed() {
2138        if (mNativeContentViewCore == 0) return false;
2139        return nativeCrashed(mNativeContentViewCore);
2140    }
2141
2142    private boolean isSelectionHandleShowing() {
2143        return mSelectionHandleController != null && mSelectionHandleController.isShowing();
2144    }
2145
2146    private boolean isInsertionHandleShowing() {
2147        return mInsertionHandleController != null && mInsertionHandleController.isShowing();
2148    }
2149
2150    private void updateTextHandlesForGesture(int type) {
2151        switch(type) {
2152            case ContentViewGestureHandler.GESTURE_DOUBLE_TAP:
2153            case ContentViewGestureHandler.GESTURE_SCROLL_START:
2154            case ContentViewGestureHandler.GESTURE_FLING_START:
2155            case ContentViewGestureHandler.GESTURE_PINCH_BEGIN:
2156                temporarilyHideTextHandles();
2157                break;
2158
2159            default:
2160                break;
2161        }
2162    }
2163
2164    // Makes the insertion/selection handles invisible. They will fade back in shortly after the
2165    // last call to scheduleTextHandleFadeIn (or temporarilyHideTextHandles).
2166    private void temporarilyHideTextHandles() {
2167        if (isSelectionHandleShowing()) {
2168            mSelectionHandleController.setHandleVisibility(HandleView.INVISIBLE);
2169        }
2170        if (isInsertionHandleShowing()) {
2171            mInsertionHandleController.setHandleVisibility(HandleView.INVISIBLE);
2172        }
2173        scheduleTextHandleFadeIn();
2174    }
2175
2176    private boolean allowTextHandleFadeIn() {
2177        if (mContentViewGestureHandler.isNativeScrolling() ||
2178                mContentViewGestureHandler.isNativePinching()) {
2179            return false;
2180        }
2181
2182        if (mPopupZoomer.isShowing()) return false;
2183
2184        return true;
2185    }
2186
2187    // Cancels any pending fade in and schedules a new one.
2188    private void scheduleTextHandleFadeIn() {
2189        if (!isInsertionHandleShowing() && !isSelectionHandleShowing()) return;
2190
2191        if (mDeferredHandleFadeInRunnable == null) {
2192            mDeferredHandleFadeInRunnable = new Runnable() {
2193                @Override
2194                public void run() {
2195                    if (!allowTextHandleFadeIn()) {
2196                        // Delay fade in until it is allowed.
2197                        scheduleTextHandleFadeIn();
2198                    } else {
2199                        if (isSelectionHandleShowing()) {
2200                            mSelectionHandleController.beginHandleFadeIn();
2201                        }
2202                        if (isInsertionHandleShowing()) {
2203                            mInsertionHandleController.beginHandleFadeIn();
2204                        }
2205                    }
2206                }
2207            };
2208        }
2209
2210        mContainerView.removeCallbacks(mDeferredHandleFadeInRunnable);
2211        mContainerView.postDelayed(mDeferredHandleFadeInRunnable, TEXT_HANDLE_FADE_IN_DELAY);
2212    }
2213
2214    /**
2215     * Shows the IME if the focused widget could accept text input.
2216     */
2217    public void showImeIfNeeded() {
2218        if (mNativeContentViewCore != 0) nativeShowImeIfNeeded(mNativeContentViewCore);
2219    }
2220
2221    public void setUpdateFrameInfoListener(UpdateFrameInfoListener updateFrameInfoListener) {
2222        mUpdateFrameInfoListener = updateFrameInfoListener;
2223    }
2224
2225    @SuppressWarnings("unused")
2226    @CalledByNative
2227    private void updateFrameInfo(
2228            float scrollOffsetX, float scrollOffsetY,
2229            float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
2230            float contentWidth, float contentHeight,
2231            float viewportWidth, float viewportHeight,
2232            float controlsOffsetYCss, float contentOffsetYCss,
2233            float overdrawBottomHeightCss) {
2234        TraceEvent.instant("ContentViewCore:updateFrameInfo");
2235        // Adjust contentWidth/Height to be always at least as big as
2236        // the actual viewport (as set by onSizeChanged).
2237        contentWidth = Math.max(contentWidth,
2238                mRenderCoordinates.fromPixToLocalCss(mViewportWidthPix));
2239        contentHeight = Math.max(contentHeight,
2240                mRenderCoordinates.fromPixToLocalCss(mViewportHeightPix));
2241
2242        final float contentOffsetYPix = mRenderCoordinates.fromDipToPix(contentOffsetYCss);
2243
2244        final boolean contentSizeChanged =
2245                contentWidth != mRenderCoordinates.getContentWidthCss()
2246                || contentHeight != mRenderCoordinates.getContentHeightCss();
2247        final boolean scaleLimitsChanged =
2248                minPageScaleFactor != mRenderCoordinates.getMinPageScaleFactor()
2249                || maxPageScaleFactor != mRenderCoordinates.getMaxPageScaleFactor();
2250        final boolean pageScaleChanged =
2251                pageScaleFactor != mRenderCoordinates.getPageScaleFactor();
2252        final boolean scrollChanged =
2253                pageScaleChanged
2254                || scrollOffsetX != mRenderCoordinates.getScrollX()
2255                || scrollOffsetY != mRenderCoordinates.getScrollY();
2256        final boolean contentOffsetChanged =
2257                contentOffsetYPix != mRenderCoordinates.getContentOffsetYPix();
2258
2259        final boolean needHidePopupZoomer = contentSizeChanged || scrollChanged;
2260        final boolean needUpdateZoomControls = scaleLimitsChanged || scrollChanged;
2261        final boolean needTemporarilyHideHandles = scrollChanged;
2262
2263        if (needHidePopupZoomer) mPopupZoomer.hide(true);
2264
2265        if (pageScaleChanged) {
2266            // This function should be called back from native as soon
2267            // as the scroll is applied to the backbuffer.  We should only
2268            // update mNativeScrollX/Y here for consistency.
2269            getContentViewClient().onScaleChanged(
2270                    mRenderCoordinates.getPageScaleFactor(), pageScaleFactor);
2271        }
2272
2273        mRenderCoordinates.updateFrameInfo(
2274                scrollOffsetX, scrollOffsetY,
2275                contentWidth, contentHeight,
2276                viewportWidth, viewportHeight,
2277                pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
2278                contentOffsetYPix);
2279
2280        if ((contentSizeChanged || pageScaleChanged) && mUpdateFrameInfoListener != null) {
2281            mUpdateFrameInfoListener.onFrameInfoUpdated(
2282                    contentWidth, contentHeight, pageScaleFactor);
2283        }
2284
2285        if (needTemporarilyHideHandles) temporarilyHideTextHandles();
2286        if (needUpdateZoomControls) mZoomControlsDelegate.updateZoomControls();
2287        if (contentOffsetChanged) updateHandleScreenPositions();
2288
2289        // Update offsets for fullscreen.
2290        final float deviceScale = mRenderCoordinates.getDeviceScaleFactor();
2291        final float controlsOffsetPix = controlsOffsetYCss * deviceScale;
2292        final float overdrawBottomHeightPix = overdrawBottomHeightCss * deviceScale;
2293        getContentViewClient().onOffsetsForFullscreenChanged(
2294                controlsOffsetPix, contentOffsetYPix, overdrawBottomHeightPix);
2295
2296        mPendingRendererFrame = true;
2297        if (mBrowserAccessibilityManager != null) {
2298            mBrowserAccessibilityManager.notifyFrameInfoInitialized();
2299        }
2300
2301        // Update geometry for external video surface.
2302        getContentViewClient().onGeometryChanged(-1, null);
2303    }
2304
2305    @SuppressWarnings("unused")
2306    @CalledByNative
2307    private void updateImeAdapter(int nativeImeAdapterAndroid, int textInputType,
2308            String text, int selectionStart, int selectionEnd,
2309            int compositionStart, int compositionEnd, boolean showImeIfNeeded) {
2310        TraceEvent.begin();
2311        mSelectionEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
2312
2313        if (mActionMode != null) mActionMode.invalidate();
2314
2315        mImeAdapter.attachAndShowIfNeeded(nativeImeAdapterAndroid, textInputType,
2316                selectionStart, selectionEnd, showImeIfNeeded);
2317
2318        if (mInputConnection != null) {
2319            mInputConnection.setEditableText(text, selectionStart, selectionEnd,
2320                    compositionStart, compositionEnd);
2321        }
2322        TraceEvent.end();
2323    }
2324
2325    @SuppressWarnings("unused")
2326    @CalledByNative
2327    private void processImeBatchStateAck(boolean isBegin) {
2328        if (mInputConnection == null) return;
2329        mInputConnection.setIgnoreTextInputStateUpdates(isBegin);
2330    }
2331
2332    @SuppressWarnings("unused")
2333    @CalledByNative
2334    private void setTitle(String title) {
2335        getContentViewClient().onUpdateTitle(title);
2336    }
2337
2338    /**
2339     * Called (from native) when the <select> popup needs to be shown.
2340     * @param items           Items to show.
2341     * @param enabled         POPUP_ITEM_TYPEs for items.
2342     * @param multiple        Whether the popup menu should support multi-select.
2343     * @param selectedIndices Indices of selected items.
2344     */
2345    @SuppressWarnings("unused")
2346    @CalledByNative
2347    private void showSelectPopup(String[] items, int[] enabled, boolean multiple,
2348            int[] selectedIndices) {
2349        SelectPopupDialog.show(this, items, enabled, multiple, selectedIndices);
2350    }
2351
2352    @SuppressWarnings("unused")
2353    @CalledByNative
2354    private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
2355        mPopupZoomer.setBitmap(zoomedBitmap);
2356        mPopupZoomer.show(targetRect);
2357        temporarilyHideTextHandles();
2358    }
2359
2360    @SuppressWarnings("unused")
2361    @CalledByNative
2362    private SmoothScroller createSmoothScroller(boolean scrollDown, int mouseEventX,
2363            int mouseEventY) {
2364        return new SmoothScroller(this, scrollDown, mouseEventX, mouseEventY);
2365    }
2366
2367    @SuppressWarnings("unused")
2368    @CalledByNative
2369    private void onSelectionChanged(String text) {
2370        mLastSelectedText = text;
2371    }
2372
2373    @SuppressWarnings("unused")
2374    @CalledByNative
2375    private void onSelectionBoundsChanged(Rect anchorRectDip, int anchorDir, Rect focusRectDip,
2376            int focusDir, boolean isAnchorFirst) {
2377        // All coordinates are in DIP.
2378        int x1 = anchorRectDip.left;
2379        int y1 = anchorRectDip.bottom;
2380        int x2 = focusRectDip.left;
2381        int y2 = focusRectDip.bottom;
2382
2383        if (x1 != x2 || y1 != y2 ||
2384                (mSelectionHandleController != null && mSelectionHandleController.isDragging())) {
2385            if (mInsertionHandleController != null) {
2386                mInsertionHandleController.hide();
2387            }
2388            if (isAnchorFirst) {
2389                mStartHandlePoint.setLocalDip(x1, y1);
2390                mEndHandlePoint.setLocalDip(x2, y2);
2391            } else {
2392                mStartHandlePoint.setLocalDip(x2, y2);
2393                mEndHandlePoint.setLocalDip(x1, y1);
2394            }
2395
2396            getSelectionHandleController().onSelectionChanged(anchorDir, focusDir);
2397            updateHandleScreenPositions();
2398            mHasSelection = true;
2399        } else {
2400            mUnselectAllOnActionModeDismiss = false;
2401            hideSelectActionBar();
2402            if (x1 != 0 && y1 != 0 && mSelectionEditable) {
2403                // Selection is a caret, and a text field is focused.
2404                if (mSelectionHandleController != null) {
2405                    mSelectionHandleController.hide();
2406                }
2407                mInsertionHandlePoint.setLocalDip(x1, y1);
2408
2409                getInsertionHandleController().onCursorPositionChanged();
2410                updateHandleScreenPositions();
2411                InputMethodManager manager = (InputMethodManager)
2412                        getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
2413                if (manager.isWatchingCursor(mContainerView)) {
2414                    final int xPix = (int) mInsertionHandlePoint.getXPix();
2415                    final int yPix = (int) mInsertionHandlePoint.getYPix();
2416                    manager.updateCursor(mContainerView, xPix, yPix, xPix, yPix);
2417                }
2418            } else {
2419                // Deselection
2420                if (mSelectionHandleController != null) {
2421                    mSelectionHandleController.hideAndDisallowAutomaticShowing();
2422                }
2423                if (mInsertionHandleController != null) {
2424                    mInsertionHandleController.hideAndDisallowAutomaticShowing();
2425                }
2426            }
2427            mHasSelection = false;
2428        }
2429    }
2430
2431    @SuppressWarnings("unused")
2432    @CalledByNative
2433    private static void onEvaluateJavaScriptResult(
2434            String jsonResult, JavaScriptCallback callback) {
2435        callback.handleJavaScriptResult(jsonResult);
2436    }
2437
2438    @SuppressWarnings("unused")
2439    @CalledByNative
2440    private void showPastePopup(int xDip, int yDip) {
2441        mInsertionHandlePoint.setLocalDip(xDip, yDip);
2442        getInsertionHandleController().showHandle();
2443        updateHandleScreenPositions();
2444        getInsertionHandleController().showHandleWithPastePopup();
2445    }
2446
2447    @SuppressWarnings("unused")
2448    @CalledByNative
2449    private void onRenderProcessSwap(int oldPid, int newPid) {
2450        assert mPid == oldPid || mPid == newPid;
2451        if (mAttachedToWindow && oldPid != newPid) {
2452            ChildProcessLauncher.unbindAsHighPriority(oldPid);
2453            ChildProcessLauncher.bindAsHighPriority(newPid);
2454        }
2455
2456        // We want to remove the initial binding even if the ContentView is not attached, so that
2457        // renderers for ContentViews loading in background do not retain the high priority.
2458        ChildProcessLauncher.removeInitialBinding(newPid);
2459        mPid = newPid;
2460    }
2461
2462    @SuppressWarnings("unused")
2463    @CalledByNative
2464    private void onWebContentsConnected() {
2465        if (mImeAdapter != null &&
2466                !mImeAdapter.isNativeImeAdapterAttached() && mNativeContentViewCore != 0) {
2467            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2468        }
2469    }
2470
2471    @SuppressWarnings("unused")
2472    @CalledByNative
2473    private void onWebContentsSwapped() {
2474        if (mImeAdapter != null && mNativeContentViewCore != 0) {
2475            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
2476        }
2477    }
2478
2479    /**
2480     * @return Whether a reload happens when this ContentView is activated.
2481     */
2482    public boolean needsReload() {
2483        return mNativeContentViewCore != 0 && nativeNeedsReload(mNativeContentViewCore);
2484    }
2485
2486    /**
2487     * @see View#hasFocus()
2488     */
2489    @CalledByNative
2490    public boolean hasFocus() {
2491        return mContainerView.hasFocus();
2492    }
2493
2494    /**
2495     * Checks whether the ContentViewCore can be zoomed in.
2496     *
2497     * @return True if the ContentViewCore can be zoomed in.
2498     */
2499    // This method uses the term 'zoom' for legacy reasons, but relates
2500    // to what chrome calls the 'page scale factor'.
2501    public boolean canZoomIn() {
2502        final float zoomInExtent = mRenderCoordinates.getMaxPageScaleFactor()
2503                - mRenderCoordinates.getPageScaleFactor();
2504        return zoomInExtent > ZOOM_CONTROLS_EPSILON;
2505    }
2506
2507    /**
2508     * Checks whether the ContentViewCore can be zoomed out.
2509     *
2510     * @return True if the ContentViewCore can be zoomed out.
2511     */
2512    // This method uses the term 'zoom' for legacy reasons, but relates
2513    // to what chrome calls the 'page scale factor'.
2514    public boolean canZoomOut() {
2515        final float zoomOutExtent = mRenderCoordinates.getPageScaleFactor()
2516                - mRenderCoordinates.getMinPageScaleFactor();
2517        return zoomOutExtent > ZOOM_CONTROLS_EPSILON;
2518    }
2519
2520    /**
2521     * Zooms in the ContentViewCore by 25% (or less if that would result in
2522     * zooming in more than possible).
2523     *
2524     * @return True if there was a zoom change, false otherwise.
2525     */
2526    // This method uses the term 'zoom' for legacy reasons, but relates
2527    // to what chrome calls the 'page scale factor'.
2528    public boolean zoomIn() {
2529        if (!canZoomIn()) {
2530            return false;
2531        }
2532        return zoomByDelta(1.25f);
2533    }
2534
2535    /**
2536     * Zooms out the ContentViewCore by 20% (or less if that would result in
2537     * zooming out more than possible).
2538     *
2539     * @return True if there was a zoom change, false otherwise.
2540     */
2541    // This method uses the term 'zoom' for legacy reasons, but relates
2542    // to what chrome calls the 'page scale factor'.
2543    public boolean zoomOut() {
2544        if (!canZoomOut()) {
2545            return false;
2546        }
2547        return zoomByDelta(0.8f);
2548    }
2549
2550    /**
2551     * Resets the zoom factor of the ContentViewCore.
2552     *
2553     * @return True if there was a zoom change, false otherwise.
2554     */
2555    // This method uses the term 'zoom' for legacy reasons, but relates
2556    // to what chrome calls the 'page scale factor'.
2557    public boolean zoomReset() {
2558        // The page scale factor is initialized to mNativeMinimumScale when
2559        // the page finishes loading. Thus sets it back to mNativeMinimumScale.
2560        if (!canZoomOut()) return false;
2561        return zoomByDelta(
2562                mRenderCoordinates.getMinPageScaleFactor()
2563                        / mRenderCoordinates.getPageScaleFactor());
2564    }
2565
2566    private boolean zoomByDelta(float delta) {
2567        if (mNativeContentViewCore == 0) {
2568            return false;
2569        }
2570
2571        long timeMs = System.currentTimeMillis();
2572        int xPix = getViewportWidthPix() / 2;
2573        int yPix = getViewportHeightPix() / 2;
2574
2575        getContentViewGestureHandler().pinchBegin(timeMs, xPix, yPix);
2576        getContentViewGestureHandler().pinchBy(timeMs, xPix, yPix, delta);
2577        getContentViewGestureHandler().pinchEnd(timeMs);
2578
2579        return true;
2580    }
2581
2582    /**
2583     * Invokes the graphical zoom picker widget for this ContentView.
2584     */
2585    @Override
2586    public void invokeZoomPicker() {
2587        mZoomControlsDelegate.invokeZoomPicker();
2588    }
2589
2590    /**
2591     * This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)}
2592     * and automatically pass in {@link JavascriptInterface} as the required annotation.
2593     *
2594     * @param object The Java object to inject into the ContentViewCore's JavaScript context.  Null
2595     *               values are ignored.
2596     * @param name   The name used to expose the instance in JavaScript.
2597     */
2598    public void addJavascriptInterface(Object object, String name) {
2599        addPossiblyUnsafeJavascriptInterface(object, name, JavascriptInterface.class);
2600    }
2601
2602    /**
2603     * This method injects the supplied Java object into the ContentViewCore.
2604     * The object is injected into the JavaScript context of the main frame,
2605     * using the supplied name. This allows the Java object to be accessed from
2606     * JavaScript. Note that that injected objects will not appear in
2607     * JavaScript until the page is next (re)loaded. For example:
2608     * <pre> view.addJavascriptInterface(new Object(), "injectedObject");
2609     * view.loadData("<!DOCTYPE html><title></title>", "text/html", null);
2610     * view.loadUrl("javascript:alert(injectedObject.toString())");</pre>
2611     * <p><strong>IMPORTANT:</strong>
2612     * <ul>
2613     * <li> addJavascriptInterface() can be used to allow JavaScript to control
2614     * the host application. This is a powerful feature, but also presents a
2615     * security risk. Use of this method in a ContentViewCore containing
2616     * untrusted content could allow an attacker to manipulate the host
2617     * application in unintended ways, executing Java code with the permissions
2618     * of the host application. Use extreme care when using this method in a
2619     * ContentViewCore which could contain untrusted content. Particular care
2620     * should be taken to avoid unintentional access to inherited methods, such
2621     * as {@link Object#getClass()}. To prevent access to inherited methods,
2622     * pass an annotation for {@code requiredAnnotation}.  This will ensure
2623     * that only methods with {@code requiredAnnotation} are exposed to the
2624     * Javascript layer.  {@code requiredAnnotation} will be passed to all
2625     * subsequently injected Java objects if any methods return an object.  This
2626     * means the same restrictions (or lack thereof) will apply.  Alternatively,
2627     * {@link #addJavascriptInterface(Object, String)} can be called, which
2628     * automatically uses the {@link JavascriptInterface} annotation.
2629     * <li> JavaScript interacts with Java objects on a private, background
2630     * thread of the ContentViewCore. Care is therefore required to maintain
2631     * thread safety.</li>
2632     * </ul></p>
2633     *
2634     * @param object             The Java object to inject into the
2635     *                           ContentViewCore's JavaScript context. Null
2636     *                           values are ignored.
2637     * @param name               The name used to expose the instance in
2638     *                           JavaScript.
2639     * @param requiredAnnotation Restrict exposed methods to ones with this
2640     *                           annotation.  If {@code null} all methods are
2641     *                           exposed.
2642     *
2643     */
2644    public void addPossiblyUnsafeJavascriptInterface(Object object, String name,
2645            Class<? extends Annotation> requiredAnnotation) {
2646        if (mNativeContentViewCore != 0 && object != null) {
2647            mJavaScriptInterfaces.put(name, object);
2648            nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation,
2649                    mRetainedJavaScriptObjects);
2650        }
2651    }
2652
2653    /**
2654     * Removes a previously added JavaScript interface with the given name.
2655     *
2656     * @param name The name of the interface to remove.
2657     */
2658    public void removeJavascriptInterface(String name) {
2659        mJavaScriptInterfaces.remove(name);
2660        if (mNativeContentViewCore != 0) {
2661            nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2662        }
2663    }
2664
2665    /**
2666     * Return the current scale of the ContentView.
2667     * @return The current page scale factor.
2668     */
2669    public float getScale() {
2670        return mRenderCoordinates.getPageScaleFactor();
2671    }
2672
2673    /**
2674     * If the view is ready to draw contents to the screen. In hardware mode,
2675     * the initialization of the surface texture may not occur until after the
2676     * view has been added to the layout. This method will return {@code true}
2677     * once the texture is actually ready.
2678     */
2679    public boolean isReady() {
2680        return nativeIsRenderWidgetHostViewReady(mNativeContentViewCore);
2681    }
2682
2683    @CalledByNative
2684    private void startContentIntent(String contentUrl) {
2685        getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2686    }
2687
2688    @Override
2689    public void onAccessibilityStateChanged(boolean enabled) {
2690        setAccessibilityState(enabled);
2691    }
2692
2693    /**
2694     * Determines whether or not this ContentViewCore can handle this accessibility action.
2695     * @param action The action to perform.
2696     * @return Whether or not this action is supported.
2697     */
2698    public boolean supportsAccessibilityAction(int action) {
2699        return mAccessibilityInjector.supportsAccessibilityAction(action);
2700    }
2701
2702    /**
2703     * Attempts to perform an accessibility action on the web content.  If the accessibility action
2704     * cannot be processed, it returns {@code null}, allowing the caller to know to call the
2705     * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value.
2706     * Otherwise the return value from this method should be used.
2707     * @param action The action to perform.
2708     * @param arguments Optional action arguments.
2709     * @return Whether the action was performed or {@code null} if the call should be delegated to
2710     *         the super {@link View} class.
2711     */
2712    public boolean performAccessibilityAction(int action, Bundle arguments) {
2713        if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2714            return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2715        }
2716
2717        return false;
2718    }
2719
2720    /**
2721     * Set the BrowserAccessibilityManager, used for native accessibility
2722     * (not script injection). This is only set when system accessibility
2723     * has been enabled.
2724     * @param manager The new BrowserAccessibilityManager.
2725     */
2726    public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
2727        mBrowserAccessibilityManager = manager;
2728    }
2729
2730    /**
2731     * Get the BrowserAccessibilityManager, used for native accessibility
2732     * (not script injection). This will return null when system accessibility
2733     * is not enabled.
2734     * @return This view's BrowserAccessibilityManager.
2735     */
2736    public BrowserAccessibilityManager getBrowserAccessibilityManager() {
2737        return mBrowserAccessibilityManager;
2738    }
2739
2740    /**
2741     * If native accessibility (not script injection) is enabled, and if this is
2742     * running on JellyBean or later, returns an AccessibilityNodeProvider that
2743     * implements native accessibility for this view. Returns null otherwise.
2744     * @return The AccessibilityNodeProvider, if available, or null otherwise.
2745     */
2746    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
2747        if (mBrowserAccessibilityManager != null) {
2748            return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
2749        } else {
2750            return null;
2751        }
2752    }
2753
2754    /**
2755     * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
2756     */
2757    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2758        // Note: this is only used by the script-injecting accessibility code.
2759        mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
2760    }
2761
2762    /**
2763     * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
2764     */
2765    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
2766        // Note: this is only used by the script-injecting accessibility code.
2767        event.setClassName(this.getClass().getName());
2768
2769        // Identify where the top-left of the screen currently points to.
2770        event.setScrollX(mRenderCoordinates.getScrollXPixInt());
2771        event.setScrollY(mRenderCoordinates.getScrollYPixInt());
2772
2773        // The maximum scroll values are determined by taking the content dimensions and
2774        // subtracting off the actual dimensions of the ChromeView.
2775        int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt());
2776        int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt());
2777        event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0);
2778
2779        // Setting the maximum scroll values requires API level 15 or higher.
2780        final int SDK_VERSION_REQUIRED_TO_SET_SCROLL = 15;
2781        if (Build.VERSION.SDK_INT >= SDK_VERSION_REQUIRED_TO_SET_SCROLL) {
2782            event.setMaxScrollX(maxScrollXPix);
2783            event.setMaxScrollY(maxScrollYPix);
2784        }
2785    }
2786
2787    /**
2788     * Returns whether accessibility script injection is enabled on the device
2789     */
2790    public boolean isDeviceAccessibilityScriptInjectionEnabled() {
2791        try {
2792            if (!mContentSettings.getJavaScriptEnabled()) {
2793                return false;
2794            }
2795
2796            int result = getContext().checkCallingOrSelfPermission(
2797                    android.Manifest.permission.INTERNET);
2798            if (result != PackageManager.PERMISSION_GRANTED) {
2799                return false;
2800            }
2801
2802            Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
2803            field.setAccessible(true);
2804            String accessibilityScriptInjection = (String) field.get(null);
2805            ContentResolver contentResolver = getContext().getContentResolver();
2806
2807            if (mAccessibilityScriptInjectionObserver == null) {
2808                ContentObserver contentObserver = new ContentObserver(new Handler()) {
2809                    public void onChange(boolean selfChange, Uri uri) {
2810                        setAccessibilityState(mAccessibilityManager.isEnabled());
2811                    }
2812                };
2813                contentResolver.registerContentObserver(
2814                    Settings.Secure.getUriFor(accessibilityScriptInjection),
2815                    false,
2816                    contentObserver);
2817                mAccessibilityScriptInjectionObserver = contentObserver;
2818            }
2819
2820            return Settings.Secure.getInt(contentResolver, accessibilityScriptInjection, 0) == 1;
2821        } catch (NoSuchFieldException e) {
2822        } catch (IllegalAccessException e) {
2823        }
2824        return false;
2825    }
2826
2827    /**
2828     * Returns whether or not accessibility injection is being used.
2829     */
2830    public boolean isInjectingAccessibilityScript() {
2831        return mAccessibilityInjector.accessibilityIsAvailable();
2832    }
2833
2834    /**
2835     * Turns browser accessibility on or off.
2836     * If |state| is |false|, this turns off both native and injected accessibility.
2837     * Otherwise, if accessibility script injection is enabled, this will enable the injected
2838     * accessibility scripts, and if it is disabled this will enable the native accessibility.
2839     */
2840    public void setAccessibilityState(boolean state) {
2841        boolean injectedAccessibility = false;
2842        boolean nativeAccessibility = false;
2843        if (state) {
2844            if (isDeviceAccessibilityScriptInjectionEnabled()) {
2845                injectedAccessibility = true;
2846            } else {
2847                nativeAccessibility = true;
2848            }
2849        }
2850        setInjectedAccessibility(injectedAccessibility);
2851        setNativeAccessibilityState(nativeAccessibility);
2852    }
2853
2854    /**
2855     * Enable or disable native accessibility features.
2856     */
2857    public void setNativeAccessibilityState(boolean enabled) {
2858        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
2859            nativeSetAccessibilityEnabled(mNativeContentViewCore, enabled);
2860        }
2861    }
2862
2863    /**
2864     * Enable or disable injected accessibility features
2865     */
2866    public void setInjectedAccessibility(boolean enabled) {
2867        mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
2868        mAccessibilityInjector.setScriptEnabled(enabled);
2869    }
2870
2871    /**
2872     * Stop any TTS notifications that are currently going on.
2873     */
2874    public void stopCurrentAccessibilityNotifications() {
2875        mAccessibilityInjector.onPageLostFocus();
2876    }
2877
2878    /**
2879     * Inform WebKit that Fullscreen mode has been exited by the user.
2880     */
2881    public void exitFullscreen() {
2882        nativeExitFullscreen(mNativeContentViewCore);
2883    }
2884
2885    /**
2886     * Changes whether hiding the top controls is enabled.
2887     *
2888     * @param enableHiding Whether hiding the top controls should be enabled or not.
2889     * @param enableShowing Whether showing the top controls should be enabled or not.
2890     * @param animate Whether the transition should be animated or not.
2891     */
2892    public void updateTopControlsState(boolean enableHiding, boolean enableShowing,
2893            boolean animate) {
2894        nativeUpdateTopControlsState(mNativeContentViewCore, enableHiding, enableShowing, animate);
2895    }
2896
2897    /**
2898     * @See android.webkit.WebView#pageDown(boolean)
2899     */
2900    public boolean pageDown(boolean bottom) {
2901        final int maxVerticalScrollPix = mRenderCoordinates.getMaxVerticalScrollPixInt();
2902        if (computeVerticalScrollOffset() >= maxVerticalScrollPix) {
2903            // We seem to already be at the bottom of the page, so no scrolling will occur.
2904            return false;
2905        }
2906
2907        if (bottom) {
2908            scrollTo(computeHorizontalScrollOffset(), maxVerticalScrollPix);
2909        } else {
2910            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_DOWN));
2911            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_PAGE_DOWN));
2912        }
2913        return true;
2914    }
2915
2916    /**
2917     * @See android.webkit.WebView#pageUp(boolean)
2918     */
2919    public boolean pageUp(boolean top) {
2920        if (computeVerticalScrollOffset() == 0) {
2921            // We seem to already be at the top of the page, so no scrolling will occur.
2922            return false;
2923        }
2924
2925        if (top) {
2926            scrollTo(computeHorizontalScrollOffset(), 0);
2927        } else {
2928            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_UP));
2929            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_PAGE_UP));
2930        }
2931        return true;
2932    }
2933
2934    /**
2935     * Callback factory method for nativeGetNavigationHistory().
2936     */
2937    @CalledByNative
2938    private void addToNavigationHistory(Object history, int index, String url, String virtualUrl,
2939            String originalUrl, String title, Bitmap favicon) {
2940        NavigationEntry entry = new NavigationEntry(
2941                index, url, virtualUrl, originalUrl, title, favicon);
2942        ((NavigationHistory) history).addEntry(entry);
2943    }
2944
2945    /**
2946     * Get a copy of the navigation history of the view.
2947     */
2948    public NavigationHistory getNavigationHistory() {
2949        NavigationHistory history = new NavigationHistory();
2950        int currentIndex = nativeGetNavigationHistory(mNativeContentViewCore, history);
2951        history.setCurrentEntryIndex(currentIndex);
2952        return history;
2953    }
2954
2955    @Override
2956    public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
2957        NavigationHistory history = new NavigationHistory();
2958        nativeGetDirectedNavigationHistory(mNativeContentViewCore, history, isForward, itemLimit);
2959        return history;
2960    }
2961
2962    /**
2963     * @return The original request URL for the current navigation entry, or null if there is no
2964     *         current entry.
2965     */
2966    public String getOriginalUrlForActiveNavigationEntry() {
2967        return nativeGetOriginalUrlForActiveNavigationEntry(mNativeContentViewCore);
2968    }
2969
2970    /**
2971     * @return The cached copy of render positions and scales.
2972     */
2973    public RenderCoordinates getRenderCoordinates() {
2974        return mRenderCoordinates;
2975    }
2976
2977    @CalledByNative
2978    private static Rect createRect(int x, int y, int right, int bottom) {
2979        return new Rect(x, y, right, bottom);
2980    }
2981
2982    public void attachExternalVideoSurface(int playerId, Surface surface) {
2983        if (mNativeContentViewCore != 0) {
2984            nativeAttachExternalVideoSurface(mNativeContentViewCore, playerId, surface);
2985        }
2986    }
2987
2988    public void detachExternalVideoSurface(int playerId) {
2989        if (mNativeContentViewCore != 0) {
2990            nativeDetachExternalVideoSurface(mNativeContentViewCore, playerId);
2991        }
2992    }
2993
2994    private boolean onAnimate(long frameTimeMicros) {
2995        if (mNativeContentViewCore == 0) return false;
2996        return nativeOnAnimate(mNativeContentViewCore, frameTimeMicros);
2997    }
2998
2999    private void animateIfNecessary(long frameTimeMicros) {
3000        if (mNeedAnimate) {
3001            mNeedAnimate = onAnimate(frameTimeMicros);
3002            if (!mNeedAnimate) setVSyncNotificationEnabled(false);
3003        }
3004    }
3005
3006    @CalledByNative
3007    private void notifyExternalSurface(
3008            int playerId, boolean isRequest, float x, float y, float width, float height) {
3009        if (isRequest) getContentViewClient().onExternalVideoSurfaceRequested(playerId);
3010        getContentViewClient().onGeometryChanged(playerId, new RectF(x, y, x + width, y + height));
3011    }
3012
3013    /**
3014     * Offer a subset of gesture events to the embedding View,
3015     * primarily for WebView compatibility.
3016     *
3017     * @param type The type of the event.
3018     *
3019     * @return true if the embedder handled the event.
3020     */
3021    private boolean offerGestureToEmbedder(int type) {
3022        if (type == ContentViewGestureHandler.GESTURE_LONG_PRESS) {
3023            return mContainerView.performLongClick();
3024        }
3025        return false;
3026    }
3027
3028    private native int nativeInit(boolean hardwareAccelerated, int webContentsPtr,
3029            int viewAndroidPtr, int windowAndroidPtr);
3030
3031    @CalledByNative
3032    private ContentVideoViewClient getContentVideoViewClient() {
3033        return mContentViewClient.getContentVideoViewClient();
3034    }
3035
3036    private native void nativeOnJavaContentViewCoreDestroyed(int nativeContentViewCoreImpl);
3037
3038    private native void nativeLoadUrl(
3039            int nativeContentViewCoreImpl,
3040            String url,
3041            int loadUrlType,
3042            int transitionType,
3043            int uaOverrideOption,
3044            String extraHeaders,
3045            byte[] postData,
3046            String baseUrlForDataUrl,
3047            String virtualUrlForDataUrl,
3048            boolean canLoadLocalResources);
3049
3050    private native String nativeGetURL(int nativeContentViewCoreImpl);
3051
3052    private native String nativeGetTitle(int nativeContentViewCoreImpl);
3053
3054    private native void nativeShowInterstitialPage(
3055            int nativeContentViewCoreImpl, String url, int nativeInterstitialPageDelegateAndroid);
3056    private native boolean nativeIsShowingInterstitialPage(int nativeContentViewCoreImpl);
3057
3058    private native boolean nativeIsIncognito(int nativeContentViewCoreImpl);
3059
3060    // Returns true if the native side crashed so that java side can draw a sad tab.
3061    private native boolean nativeCrashed(int nativeContentViewCoreImpl);
3062
3063    private native void nativeSetFocus(int nativeContentViewCoreImpl, boolean focused);
3064
3065    private native void nativeSendOrientationChangeEvent(
3066            int nativeContentViewCoreImpl, int orientation);
3067
3068    // All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
3069    private native boolean nativeSendTouchEvent(
3070            int nativeContentViewCoreImpl, long timeMs, int action, TouchPoint[] pts);
3071
3072    private native int nativeSendMouseMoveEvent(
3073            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3074
3075    private native int nativeSendMouseWheelEvent(
3076            int nativeContentViewCoreImpl, long timeMs, float x, float y, float verticalAxis);
3077
3078    private native void nativeScrollBegin(
3079            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3080
3081    private native void nativeScrollEnd(int nativeContentViewCoreImpl, long timeMs);
3082
3083    private native void nativeScrollBy(
3084            int nativeContentViewCoreImpl, long timeMs, float x, float y,
3085            float deltaX, float deltaY, boolean lastInputEventForVSync);
3086
3087    private native void nativeFlingStart(
3088            int nativeContentViewCoreImpl, long timeMs, float x, float y, float vx, float vy);
3089
3090    private native void nativeFlingCancel(int nativeContentViewCoreImpl, long timeMs);
3091
3092    private native void nativeSingleTap(
3093            int nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
3094
3095    private native void nativeSingleTapUnconfirmed(
3096            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3097
3098    private native void nativeShowPressState(
3099            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3100
3101    private native void nativeShowPressCancel(
3102            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3103
3104    private native void nativeDoubleTap(
3105            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3106
3107    private native void nativeLongPress(
3108            int nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
3109
3110    private native void nativeLongTap(
3111            int nativeContentViewCoreImpl, long timeMs, float x, float y, boolean linkPreviewTap);
3112
3113    private native void nativePinchBegin(
3114            int nativeContentViewCoreImpl, long timeMs, float x, float y);
3115
3116    private native void nativePinchEnd(int nativeContentViewCoreImpl, long timeMs);
3117
3118    private native void nativePinchBy(int nativeContentViewCoreImpl, long timeMs,
3119            float anchorX, float anchorY, float deltaScale, boolean lastInputEventForVSync);
3120
3121    private native void nativeSelectBetweenCoordinates(
3122            int nativeContentViewCoreImpl, float x1, float y1, float x2, float y2);
3123
3124    private native void nativeMoveCaret(int nativeContentViewCoreImpl, float x, float y);
3125
3126    private native boolean nativeCanGoBack(int nativeContentViewCoreImpl);
3127    private native boolean nativeCanGoForward(int nativeContentViewCoreImpl);
3128    private native boolean nativeCanGoToOffset(int nativeContentViewCoreImpl, int offset);
3129    private native void nativeGoBack(int nativeContentViewCoreImpl);
3130    private native void nativeGoForward(int nativeContentViewCoreImpl);
3131    private native void nativeGoToOffset(int nativeContentViewCoreImpl, int offset);
3132    private native void nativeGoToNavigationIndex(int nativeContentViewCoreImpl, int index);
3133
3134    private native void nativeStopLoading(int nativeContentViewCoreImpl);
3135
3136    private native void nativeReload(int nativeContentViewCoreImpl);
3137
3138    private native void nativeCancelPendingReload(int nativeContentViewCoreImpl);
3139
3140    private native void nativeContinuePendingReload(int nativeContentViewCoreImpl);
3141
3142    private native void nativeSelectPopupMenuItems(int nativeContentViewCoreImpl, int[] indices);
3143
3144    private native void nativeScrollFocusedEditableNodeIntoView(int nativeContentViewCoreImpl);
3145    private native void nativeUndoScrollFocusedEditableNodeIntoView(int nativeContentViewCoreImpl);
3146    private native boolean nativeNeedsReload(int nativeContentViewCoreImpl);
3147
3148    private native void nativeClearHistory(int nativeContentViewCoreImpl);
3149
3150    private native void nativeEvaluateJavaScript(int nativeContentViewCoreImpl,
3151            String script, JavaScriptCallback callback);
3152
3153    private native int nativeGetNativeImeAdapter(int nativeContentViewCoreImpl);
3154
3155    private native int nativeGetCurrentRenderProcessId(int nativeContentViewCoreImpl);
3156
3157    private native int nativeGetBackgroundColor(int nativeContentViewCoreImpl);
3158
3159    private native void nativeOnShow(int nativeContentViewCoreImpl);
3160    private native void nativeOnHide(int nativeContentViewCoreImpl);
3161
3162    private native void nativeSetUseDesktopUserAgent(int nativeContentViewCoreImpl,
3163            boolean enabled, boolean reloadOnChange);
3164    private native boolean nativeGetUseDesktopUserAgent(int nativeContentViewCoreImpl);
3165
3166    private native void nativeClearSslPreferences(int nativeContentViewCoreImpl);
3167
3168    private native void nativeAddJavascriptInterface(int nativeContentViewCoreImpl, Object object,
3169            String name, Class requiredAnnotation, HashSet<Object> retainedObjectSet);
3170
3171    private native void nativeRemoveJavascriptInterface(int nativeContentViewCoreImpl, String name);
3172
3173    private native int nativeGetNavigationHistory(int nativeContentViewCoreImpl, Object context);
3174    private native void nativeGetDirectedNavigationHistory(int nativeContentViewCoreImpl,
3175            Object context, boolean isForward, int maxEntries);
3176    private native String nativeGetOriginalUrlForActiveNavigationEntry(
3177            int nativeContentViewCoreImpl);
3178
3179    private native void nativeUpdateVSyncParameters(int nativeContentViewCoreImpl,
3180            long timebaseMicros, long intervalMicros);
3181
3182    private native void nativeOnVSync(int nativeContentViewCoreImpl, long frameTimeMicros);
3183
3184    private native boolean nativeOnAnimate(int nativeContentViewCoreImpl, long frameTimeMicros);
3185
3186    private native boolean nativePopulateBitmapFromCompositor(int nativeContentViewCoreImpl,
3187            Bitmap bitmap);
3188
3189    private native void nativeWasResized(int nativeContentViewCoreImpl);
3190
3191    private native boolean nativeIsRenderWidgetHostViewReady(int nativeContentViewCoreImpl);
3192
3193    private native void nativeExitFullscreen(int nativeContentViewCoreImpl);
3194    private native void nativeUpdateTopControlsState(int nativeContentViewCoreImpl,
3195            boolean enableHiding, boolean enableShowing, boolean animate);
3196
3197    private native void nativeShowImeIfNeeded(int nativeContentViewCoreImpl);
3198
3199    private native void nativeAttachExternalVideoSurface(
3200            int nativeContentViewCoreImpl, int playerId, Surface surface);
3201
3202    private native void nativeDetachExternalVideoSurface(
3203            int nativeContentViewCoreImpl, int playerId);
3204
3205    private native void nativeSetAccessibilityEnabled(
3206            int nativeContentViewCoreImpl, boolean enabled);
3207}
3208