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