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