ContentViewCore.java revision 5821806d5e7f356e8fa4b058a389a808ea183019
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.app.ActivityManager;
9import android.content.Context;
10import android.content.pm.ActivityInfo;
11import android.content.pm.PackageManager;
12import android.content.res.Configuration;
13import android.graphics.Bitmap;
14import android.graphics.Canvas;
15import android.graphics.Color;
16import android.graphics.PointF;
17import android.graphics.Rect;
18import android.os.Build;
19import android.os.Bundle;
20import android.os.Handler;
21import android.os.ResultReceiver;
22import android.os.SystemClock;
23import android.util.Log;
24import android.util.Pair;
25import android.view.ActionMode;
26import android.view.InputDevice;
27import android.view.KeyEvent;
28import android.view.MotionEvent;
29import android.view.View;
30import android.view.ViewGroup;
31import android.view.Surface;
32import android.view.Window;
33import android.view.WindowManager;
34import android.view.accessibility.AccessibilityEvent;
35import android.view.accessibility.AccessibilityNodeInfo;
36import android.view.inputmethod.EditorInfo;
37import android.view.inputmethod.InputConnection;
38import android.view.inputmethod.InputMethodManager;
39
40import org.chromium.base.CalledByNative;
41import org.chromium.base.JNINamespace;
42import org.chromium.base.WeakContext;
43import org.chromium.content.app.AppResource;
44import org.chromium.content.browser.ContentViewGestureHandler.MotionEventDelegate;
45import org.chromium.content.browser.accessibility.AccessibilityInjector;
46import org.chromium.content.common.TraceEvent;
47import org.chromium.ui.gfx.NativeWindow;
48
49/**
50 * Provides a Java-side 'wrapper' around a WebContent (native) instance.
51 * Contains all the major functionality necessary to manage the lifecycle of a ContentView without
52 * being tied to the view system.
53 */
54@JNINamespace("content")
55public class ContentViewCore implements MotionEventDelegate {
56    private static final String TAG = ContentViewCore.class.getName();
57
58    // The following constants match the ones in chrome/common/page_transition_types.h.
59    // Add more if you need them.
60    public static final int PAGE_TRANSITION_LINK = 0;
61    public static final int PAGE_TRANSITION_TYPED = 1;
62    public static final int PAGE_TRANSITION_AUTO_BOOKMARK = 2;
63    public static final int PAGE_TRANSITION_START_PAGE = 6;
64
65    // Used when ContentView implements a standalone View.
66    public static final int PERSONALITY_VIEW = 0;
67    // Used for Chrome.
68    public static final int PERSONALITY_CHROME = 1;
69
70    // Used to avoid enabling zooming in / out if resulting zooming will
71    // produce little visible difference.
72    private static final float ZOOM_CONTROLS_EPSILON = 0.007f;
73
74    // To avoid checkerboard, we clamp the fling velocity based on the maximum number of tiles
75    // should be allowed to upload per 100ms.
76    private final int mMaxNumUploadTiles;
77
78    // Personality of the ContentView.
79    private final int mPersonality;
80
81    /**
82     * Interface that consumers of {@link ContentViewCore} must implement to allow the proper
83     * dispatching of view methods through the containing view.
84     *
85     * <p>
86     * All methods with the "super_" prefix should be routed to the parent of the
87     * implementing container view.
88     */
89    @SuppressWarnings("javadoc")
90    public static interface InternalAccessDelegate {
91        /**
92         * @see View#drawChild(Canvas, View, long)
93         */
94        boolean drawChild(Canvas canvas, View child, long drawingTime);
95
96        /**
97         * @see View#onKeyUp(keyCode, KeyEvent)
98         */
99        boolean super_onKeyUp(int keyCode, KeyEvent event);
100
101        /**
102         * @see View#dispatchKeyEventPreIme(KeyEvent)
103         */
104        boolean super_dispatchKeyEventPreIme(KeyEvent event);
105
106        /**
107         * @see View#dispatchKeyEvent(KeyEvent)
108         */
109        boolean super_dispatchKeyEvent(KeyEvent event);
110
111        /**
112         * @see View#onGenericMotionEvent(MotionEvent)
113         */
114        boolean super_onGenericMotionEvent(MotionEvent event);
115
116        /**
117         * @see View#onConfigurationChanged(Configuration)
118         */
119        void super_onConfigurationChanged(Configuration newConfig);
120
121        /**
122         * @see View#onScrollChanged(int, int, int, int)
123         */
124        void onScrollChanged(int l, int t, int oldl, int oldt);
125
126        /**
127         * @see View#awakenScrollBars()
128         */
129        boolean awakenScrollBars();
130
131        /**
132         * @see View#awakenScrollBars(int, boolean)
133         */
134        boolean super_awakenScrollBars(int startDelay, boolean invalidate);
135    }
136
137    private final Context mContext;
138    private ViewGroup mContainerView;
139    private InternalAccessDelegate mContainerViewInternals;
140    private WebContentsObserverAndroid mWebContentsObserver;
141
142    private ContentViewClient mContentViewClient;
143
144    private ContentSettings mContentSettings;
145
146    // Native pointer to C++ ContentViewCoreImpl object which will be set by nativeInit().
147    private int mNativeContentViewCore = 0;
148
149    // The vsync monitor is used to lock the compositor to the display refresh rate.
150    private VSyncMonitor mVSyncMonitor;
151
152    // The VSyncMonitor gives the timebase for the actual vsync, but we don't want render until
153    // we have had a chance for input events to propagate to the compositor thread. This takes
154    // 3 ms typically, so we adjust the vsync timestamps forward by a bit to give input events a
155    // chance to arrive.
156    private static final long INPUT_EVENT_LAG_FROM_VSYNC_MICROSECONDS = 3200;
157
158    private ContentViewGestureHandler mContentViewGestureHandler;
159    private ZoomManager mZoomManager;
160
161    // Currently ContentView's scrolling is handled by the native side. We keep a cached copy of the
162    // scroll offset and content size so that we can display the scrollbar correctly. In the future,
163    // we may switch to tile rendering and handle the scrolling in the Java level.
164
165    // Cached scroll offset from the native
166    private int mNativeScrollX;
167    private int mNativeScrollY;
168
169    // Cached content size from the native
170    private int mContentWidth;
171    private int mContentHeight;
172
173    // Cached size of the viewport
174    private int mViewportWidth;
175    private int mViewportHeight;
176
177    // Cached page scale factor from native
178    private float mNativePageScaleFactor = 1.0f;
179    private float mNativeMinimumScale = 1.0f;
180    private float mNativeMaximumScale = 1.0f;
181
182    private PopupZoomer mPopupZoomer;
183
184    private Runnable mFakeMouseMoveRunnable = null;
185
186    // Only valid when focused on a text / password field.
187    private ImeAdapter mImeAdapter;
188    private ImeAdapter.AdapterInputConnection mInputConnection;
189
190    private SelectionHandleController mSelectionHandleController;
191    private InsertionHandleController mInsertionHandleController;
192    // These offsets in document space with page scale normalized to 1.0.
193    private final PointF mStartHandleNormalizedPoint = new PointF();
194    private final PointF mEndHandleNormalizedPoint = new PointF();
195    private final PointF mInsertionHandleNormalizedPoint = new PointF();
196
197    // Tracks whether a selection is currently active.  When applied to selected text, indicates
198    // whether the last selected text is still highlighted.
199    private boolean mHasSelection;
200    private String mLastSelectedText;
201    private boolean mSelectionEditable;
202    private ActionMode mActionMode;
203
204    // Delegate that will handle GET downloads, and be notified of completion of POST downloads.
205    private ContentViewDownloadDelegate mDownloadDelegate;
206
207    // Whether a physical keyboard is connected.
208    private boolean mKeyboardConnected;
209
210    private final VSyncMonitor.Listener mVSyncListener = new VSyncMonitor.Listener() {
211        @Override
212        public void onVSync(VSyncMonitor monitor, long vsyncTimeMicros) {
213            TraceEvent.instant("VSync");
214            if (mNativeContentViewCore == 0 || mVSyncMonitor == null) {
215                return;
216            }
217            // Compensate for input event lag. Input events are delivered immediately on pre-JB
218            // releases, so this adjustment is only done for later versions.
219            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
220                vsyncTimeMicros += INPUT_EVENT_LAG_FROM_VSYNC_MICROSECONDS;
221            }
222            nativeUpdateVSyncParameters(mNativeContentViewCore, vsyncTimeMicros,
223                                        mVSyncMonitor.getVSyncPeriodInMicroseconds());
224        }
225    };
226
227    // The AccessibilityInjector that handles loading Accessibility scripts into the web page.
228    private AccessibilityInjector mAccessibilityInjector;
229
230    // Temporary notification to tell onSizeChanged to focus a form element,
231    // because the OSK was just brought up.
232    private boolean mFocusOnNextSizeChanged = false;
233    private boolean mUnfocusOnNextSizeChanged = false;
234
235    private boolean mNeedUpdateOrientationChanged;
236
237    // Used to keep track of whether we should try to undo the last zoom-to-textfield operation.
238    private boolean mScrolledAndZoomedFocusedEditableNode = false;
239
240    // Whether we use hardware-accelerated drawing.
241    private boolean mHardwareAccelerated = false;
242
243    /**
244     * Enable multi-process ContentView. This should be called by the application before
245     * constructing any ContentView instances. If enabled, ContentView will run renderers in
246     * separate processes up to the number of processes specified by maxRenderProcesses. If this is
247     * not called then the default is to run the renderer in the main application on a separate
248     * thread.
249     *
250     * @param context Context used to obtain the application context.
251     * @param maxRendererProcesses Limit on the number of renderers to use. Each tab runs in its own
252     * process until the maximum number of processes is reached. The special value of
253     * MAX_RENDERERS_SINGLE_PROCESS requests single-process mode where the renderer will run in the
254     * application process in a separate thread. If the special value MAX_RENDERERS_AUTOMATIC is
255     * used then the number of renderers will be determined based on the device memory class. The
256     * maximum number of allowed renderers is capped by MAX_RENDERERS_LIMIT.
257     * @return Whether the process actually needed to be initialized (false if already running).
258     */
259    public static boolean enableMultiProcess(Context context, int maxRendererProcesses) {
260        return AndroidBrowserProcess.initContentViewProcess(context, maxRendererProcesses);
261    }
262
263    /**
264     * Initialize the process as the platform browser. This must be called before accessing
265     * ContentView in order to treat this as a Chromium browser process.
266     *
267     * @param context Context used to obtain the application context.
268     * @param maxRendererProcesses Same as ContentView.enableMultiProcess()
269     * @return Whether the process actually needed to be initialized (false if already running).
270     */
271    public static boolean initChromiumBrowserProcess(Context context, int maxRendererProcesses) {
272        return AndroidBrowserProcess.initChromiumBrowserProcess(context, maxRendererProcesses);
273    }
274
275    /**
276     * Constructs a new ContentViewCore. Embedders must call initialize() after constructing
277     * a ContentViewCore and before using it.
278     *
279     * @param context The context used to create this.
280     * @param personality The type of ContentViewCore being created.
281     */
282    public ContentViewCore(Context context, int personality) {
283        mContext = context;
284
285        // All application resources must be registered by the time the content view is created.
286        // This should be omitted in final release builds where assertions are disabled.
287        // TODO(leandrogracia): re-enable this as soon as crbug.com/136704 is fixed.
288        // assert AppResource.verifyResourceRegistration();
289
290        WeakContext.initializeWeakContext(context);
291        // By default, ContentView will initialize single process mode. The call to
292        // initContentViewProcess below is ignored if either the ContentView host called
293        // enableMultiProcess() or the platform browser called initChromiumBrowserProcess().
294        AndroidBrowserProcess.initContentViewProcess(
295                context, AndroidBrowserProcess.MAX_RENDERERS_SINGLE_PROCESS);
296
297        mPersonality = personality;
298        HeapStatsLogger.init(mContext.getApplicationContext());
299
300        // We should set this constant based on the GPU performance. As it doesn't exist in the
301        // framework yet, we use the memory class as an indicator. Here are some good values that
302        // we determined via manual experimentation:
303        //
304        // Device            Screen size        Memory class   Tiles per 100ms
305        // ================= ================== ============== =====================
306        // Nexus S            480 x  800         128            9 (3 rows portrait)
307        // Galaxy Nexus       720 x 1280         256           12 (3 rows portrait)
308        // Nexus 7           1280 x  800         384           18 (3 rows landscape)
309        // Nexus 10          2560 x 1600         512           44 (4 rows landscape)
310        //
311        // Here is a spreadsheet with the data, plus a curve fit:
312        // https://docs.google.com/a/chromium.org/spreadsheet/pub?key=0AlNYk7HM2CgQdG1vUWRVWkU3ODRTc1B2SVF3ZTJBUkE&output=html
313        // That gives us tiles-per-100ms of 8, 13, 22, 37 for the devices listed above.
314        // Not too bad, and it should behave reasonably sensibly for unknown devices.
315        // If you want to tweak these constants, please update the spreadsheet appropriately.
316        //
317        // The curve is y = b * m^x, with coefficients as follows:
318        double b = 4.70009671080384;
319        double m = 1.00404437546897;
320        int memoryClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE))
321                .getLargeMemoryClass();
322        mMaxNumUploadTiles = (int) Math.round(b * Math.pow(m, memoryClass));
323    }
324
325    /**
326     * @return The context used for creating this ContentViewCore.
327     */
328    public Context getContext() {
329        return mContext;
330    }
331
332    /**
333     * @return The ViewGroup that all view actions of this ContentViewCore should interact with.
334     */
335    protected ViewGroup getContainerView() {
336        return mContainerView;
337    }
338
339    /**
340     * Returns a delegate that can be used to add and remove views from the ContainerView.
341     *
342     * NOTE: Use with care, as not all ContentViewCore users setup their ContainerView in the same
343     * way. In particular, the Android WebView has limitations on what implementation details can
344     * be provided via a child view, as they are visible in the API and could introduce
345     * compatibility breaks with existing applications. If in doubt, contact the
346     * android_webview/OWNERS
347     *
348     * @return A ContainerViewDelegate that can be used to add and remove views.
349     */
350    @CalledByNative
351    public ContainerViewDelegate getContainerViewDelegate() {
352        return new ContainerViewDelegate() {
353            @Override
354            public void addViewToContainerView(View view) {
355                mContainerView.addView(view);
356            }
357
358            @Override
359            public void removeViewFromContainerView(View view) {
360                mContainerView.removeView(view);
361            }
362        };
363    }
364
365    private ImeAdapter createImeAdapter(Context context) {
366        return new ImeAdapter(context, getSelectionHandleController(),
367                getInsertionHandleController(),
368                new ImeAdapter.ViewEmbedder() {
369                    @Override
370                    public void onImeEvent(boolean isFinish) {
371                        getContentViewClient().onImeEvent();
372                        if (!isFinish) {
373                            undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
374                        }
375                    }
376
377                    @Override
378                    public void onSetFieldValue() {
379                        scrollFocusedEditableNodeIntoView();
380                    }
381
382                    @Override
383                    public void onDismissInput() {
384                    }
385
386                    @Override
387                    public View getAttachedView() {
388                        return mContainerView;
389                    }
390
391                    @Override
392                    public ResultReceiver getNewShowKeyboardReceiver() {
393                        return new ResultReceiver(new Handler()) {
394                            @Override
395                            public void onReceiveResult(int resultCode, Bundle resultData) {
396                                if (resultCode == InputMethodManager.RESULT_SHOWN) {
397                                    // If OSK is newly shown, delay the form focus until
398                                    // the onSizeChanged (in order to adjust relative to the
399                                    // new size).
400                                    mFocusOnNextSizeChanged = true;
401                                } else if (resultCode ==
402                                        InputMethodManager.RESULT_UNCHANGED_SHOWN) {
403                                    // If the OSK was already there, focus the form immediately.
404                                    scrollFocusedEditableNodeIntoView();
405                                } else {
406                                    undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
407                                }
408                            }
409                        };
410                    }
411                }
412        );
413    }
414
415    /**
416     * Returns true if the given Activity has hardware acceleration enabled
417     * in its manifest, or in its foreground window.
418     *
419     * TODO(husky): Remove when initialize() is refactored (see TODO there)
420     * TODO(dtrainor) This is still used by other classes.  Make sure to pull some version of this
421     * out before removing it.
422     */
423    public static boolean hasHardwareAcceleration(Activity activity) {
424        // Has HW acceleration been enabled manually in the current window?
425        Window window = activity.getWindow();
426        if (window != null) {
427            if ((window.getAttributes().flags
428                    & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) {
429                return true;
430            }
431        }
432
433        // Has HW acceleration been enabled in the manifest?
434        try {
435            ActivityInfo info = activity.getPackageManager().getActivityInfo(
436                    activity.getComponentName(), 0);
437            if ((info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
438                return true;
439            }
440        } catch (PackageManager.NameNotFoundException e) {
441            Log.e("Chrome", "getActivityInfo(self) should not fail");
442        }
443
444        return false;
445    }
446
447    /**
448     * Returns true if the given Context is a HW-accelerated Activity.
449     *
450     * TODO(husky): Remove when initialize() is refactored (see TODO there)
451     */
452    private static boolean hasHardwareAcceleration(Context context) {
453        if (context instanceof Activity) {
454            return hasHardwareAcceleration((Activity) context);
455        }
456        return false;
457    }
458
459    /**
460     *
461     * @param containerView The view that will act as a container for all views created by this.
462     * @param internalDispatcher Handles dispatching all hidden or super methods to the
463     *                           containerView.
464     * @param nativeWebContents A pointer to the native web contents.
465     * @param nativeWindow An instance of the NativeWindow.
466     * @param isAccessFromFileURLsGrantedByDefault Default WebSettings configuration.
467     */
468    // Perform important post-construction set up of the ContentViewCore.
469    // We do not require the containing view in the constructor to allow embedders to create a
470    // ContentViewCore without having fully created its containing view. The containing view
471    // is a vital component of the ContentViewCore, so embedders must exercise caution in what
472    // they do with the ContentViewCore before calling initialize().
473    // We supply the nativeWebContents pointer here rather than in the constructor to allow us
474    // to set the private browsing mode at a later point for the WebView implementation.
475    // Note that the caller remains the owner of the nativeWebContents and is responsible for
476    // deleting it after destroying the ContentViewCore.
477    public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
478            int nativeWebContents, NativeWindow nativeWindow,
479            boolean isAccessFromFileURLsGrantedByDefault) {
480        // Check whether to use hardware acceleration. This is a bit hacky, and
481        // only works if the Context is actually an Activity (as it is in the
482        // Chrome application).
483        //
484        // What we're doing here is checking whether the app has *requested*
485        // hardware acceleration by setting the appropriate flags. This does not
486        // necessarily mean we're going to *get* hardware acceleration -- that's
487        // up to the Android framework.
488        //
489        // TODO(husky): Once the native code has been updated so that the
490        // HW acceleration flag can be set dynamically (Grace is doing this),
491        // move this check into onAttachedToWindow(), where we can test for
492        // HW support directly.
493        mHardwareAccelerated = hasHardwareAcceleration(mContext);
494        mContainerView = containerView;
495        mNativeContentViewCore = nativeInit(mHardwareAccelerated, nativeWebContents,
496                nativeWindow.getNativePointer());
497        mContentSettings = new ContentSettings(
498                this, mNativeContentViewCore, isAccessFromFileURLsGrantedByDefault);
499        initializeContainerView(internalDispatcher);
500        if (mPersonality == PERSONALITY_VIEW) {
501            setAllUserAgentOverridesInHistory();
502        }
503
504        mAccessibilityInjector = AccessibilityInjector.newInstance(this);
505        mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
506
507        String contentDescription = "Web View";
508        if (AppResource.STRING_CONTENT_VIEW_CONTENT_DESCRIPTION == 0) {
509            Log.w(TAG, "Setting contentDescription to 'Web View' as no value was specified.");
510        } else {
511            contentDescription = mContext.getResources().getString(
512                    AppResource.STRING_CONTENT_VIEW_CONTENT_DESCRIPTION);
513        }
514        mContainerView.setContentDescription(contentDescription);
515        mWebContentsObserver = new WebContentsObserverAndroid(this) {
516            @Override
517            public void didStartLoading(String url) {
518                hidePopupDialog();
519            }
520        };
521    }
522
523    @CalledByNative
524    void onNativeContentViewCoreDestroyed(int nativeContentViewCore) {
525        assert nativeContentViewCore == mNativeContentViewCore;
526        mNativeContentViewCore = 0;
527    }
528
529    /**
530     * Initializes the View that will contain all Views created by the ContentViewCore.
531     *
532     * @param internalDispatcher Handles dispatching all hidden or super methods to the
533     *                           containerView.
534     */
535    private void initializeContainerView(InternalAccessDelegate internalDispatcher) {
536        TraceEvent.begin();
537        mContainerViewInternals = internalDispatcher;
538
539        mContainerView.setWillNotDraw(false);
540        mContainerView.setFocusable(true);
541        mContainerView.setFocusableInTouchMode(true);
542        mContainerView.setClickable(true);
543
544        if (mPersonality == PERSONALITY_CHROME) {
545            // Doing this in PERSONALITY_VIEW mode causes rendering problems in our
546            // current WebView test case (the HTMLViewer application).
547            // TODO(benm): Figure out why this is the case.
548            if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
549                mContainerView.setHorizontalScrollBarEnabled(false);
550                mContainerView.setVerticalScrollBarEnabled(false);
551            }
552        }
553
554        mZoomManager = new ZoomManager(mContext, this);
555        mZoomManager.updateMultiTouchSupport();
556        mContentViewGestureHandler = new ContentViewGestureHandler(mContext, this, mZoomManager);
557
558        mNativeScrollX = mNativeScrollY = 0;
559        mNativePageScaleFactor = 1.0f;
560        initPopupZoomer(mContext);
561        mImeAdapter = createImeAdapter(mContext);
562        mKeyboardConnected = mContainerView.getResources().getConfiguration().keyboard
563                != Configuration.KEYBOARD_NOKEYS;
564        mVSyncMonitor = new VSyncMonitor(mContext, mVSyncListener);
565        TraceEvent.end();
566    }
567
568    private void initPopupZoomer(Context context){
569        mPopupZoomer = new PopupZoomer(context);
570        mContainerView.addView(mPopupZoomer);
571        PopupZoomer.OnTapListener listener = new PopupZoomer.OnTapListener() {
572            @Override
573            public boolean onSingleTap(View v, MotionEvent e) {
574                mContainerView.requestFocus();
575                if (mNativeContentViewCore != 0) {
576                    nativeSingleTap(mNativeContentViewCore, e.getEventTime(), (int) e.getX(),
577                            (int) e.getY(), true);
578                }
579                return true;
580            }
581
582            @Override
583            public boolean onLongPress(View v, MotionEvent e) {
584                if (mNativeContentViewCore != 0) {
585                    nativeLongPress(mNativeContentViewCore, e.getEventTime(), (int) e.getX(),
586                            (int) e.getY(), true);
587                }
588                return true;
589            }
590        };
591        mPopupZoomer.setOnTapListener(listener);
592    }
593
594    /**
595     * @return Whether the configured personality of this ContentView is {@link #PERSONALITY_VIEW}.
596     */
597    boolean isPersonalityView() {
598        switch (mPersonality) {
599            case PERSONALITY_VIEW:
600                return true;
601            case PERSONALITY_CHROME:
602                return false;
603            default:
604                Log.e(TAG, "Unknown ContentView personality: " + mPersonality);
605                return false;
606        }
607    }
608
609    /**
610     * Destroy the internal state of the ContentView. This method may only be
611     * called after the ContentView has been removed from the view system. No
612     * other methods may be called on this ContentView after this method has
613     * been called.
614     */
615    public void destroy() {
616        hidePopupDialog();
617        if (mVSyncMonitor != null) mVSyncMonitor.unregisterListener();
618        if (mNativeContentViewCore != 0) {
619            nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore);
620        }
621        mNativeContentViewCore = 0;
622        mContentSettings = null;
623    }
624
625    /**
626     * Returns true initially, false after destroy() has been called.
627     * It is illegal to call any other public method after destroy().
628     */
629    public boolean isAlive() {
630        return mNativeContentViewCore != 0;
631    }
632
633    /**
634     * This is only useful for passing over JNI to native code that requires ContentViewCore*.
635     * @return native ContentViewCore pointer.
636     */
637    public int getNativeContentViewCore() {
638        return mNativeContentViewCore;
639    }
640
641    /**
642     * For internal use. Throws IllegalStateException if mNativeContentView is 0.
643     * Use this to ensure we get a useful Java stack trace, rather than a native
644     * crash dump, from use-after-destroy bugs in Java code.
645     */
646    void checkIsAlive() throws IllegalStateException {
647        if (!isAlive()) {
648            throw new IllegalStateException("ContentView used after destroy() was called");
649        }
650    }
651
652    public void setContentViewClient(ContentViewClient client) {
653        if (client == null) {
654            throw new IllegalArgumentException("The client can't be null.");
655        }
656        mContentViewClient = client;
657    }
658
659    ContentViewClient getContentViewClient() {
660        if (mContentViewClient == null) {
661            // We use the Null Object pattern to avoid having to perform a null check in this class.
662            // We create it lazily because most of the time a client will be set almost immediately
663            // after ContentView is created.
664            mContentViewClient = new ContentViewClient();
665            // We don't set the native ContentViewClient pointer here on purpose. The native
666            // implementation doesn't mind a null delegate and using one is better than passing a
667            // Null Object, since we cut down on the number of JNI calls.
668        }
669        return mContentViewClient;
670    }
671
672    public int getBackgroundColor() {
673        if (mNativeContentViewCore != 0) {
674            return nativeGetBackgroundColor(mNativeContentViewCore);
675        }
676        return Color.WHITE;
677    }
678
679    public void setBackgroundColor(int color) {
680        if (mNativeContentViewCore != 0 && getBackgroundColor() != color) {
681            nativeSetBackgroundColor(mNativeContentViewCore, color);
682        }
683    }
684
685    /**
686     * Load url without fixing up the url string. Consumers of ContentView are responsible for
687     * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
688     * off during user input).
689     *
690     * @param pararms Parameters for this load.
691     */
692    public void loadUrl(LoadUrlParams params) {
693        if (mNativeContentViewCore == 0) return;
694        if (isPersonalityView()) {
695            // For WebView, always use the user agent override, which is set
696            // every time the user agent in ContentSettings is modified.
697            params.setOverrideUserAgent(LoadUrlParams.UA_OVERRIDE_TRUE);
698        }
699
700        nativeLoadUrl(mNativeContentViewCore,
701                params.mUrl,
702                params.mLoadUrlType,
703                params.mTransitionType,
704                params.mUaOverrideOption,
705                params.getExtraHeadersString(),
706                params.mPostData,
707                params.mBaseUrlForDataUrl,
708                params.mVirtualUrlForDataUrl,
709                params.mCanLoadLocalResources);
710    }
711
712    void setAllUserAgentOverridesInHistory() {
713        nativeSetAllUserAgentOverridesInHistory(mNativeContentViewCore,
714                mContentSettings.getUserAgentString());
715    }
716
717    /**
718     * Stops loading the current web contents.
719     */
720    public void stopLoading() {
721        if (mNativeContentViewCore != 0) nativeStopLoading(mNativeContentViewCore);
722    }
723
724    /**
725     * Get the URL of the current page.
726     *
727     * @return The URL of the current page.
728     */
729    public String getUrl() {
730        if (mNativeContentViewCore != 0) return nativeGetURL(mNativeContentViewCore);
731        return null;
732    }
733
734    /**
735     * Get the title of the current page.
736     *
737     * @return The title of the current page.
738     */
739    public String getTitle() {
740        if (mNativeContentViewCore != 0) return nativeGetTitle(mNativeContentViewCore);
741        return null;
742    }
743
744    @CalledByNative
745    public int getWidth() {
746        return mViewportWidth;
747    }
748
749    @CalledByNative
750    public int getHeight() {
751        return mViewportHeight;
752    }
753
754    /**
755     * @see android.webkit.WebView#getContentHeight()
756     */
757    public int getContentHeight() {
758        return (int) (mContentHeight / mNativePageScaleFactor);
759    }
760
761    /**
762     * @see android.webkit.WebView#getContentWidth()
763     */
764    public int getContentWidth() {
765        return (int) (mContentWidth / mNativePageScaleFactor);
766    }
767
768    public Bitmap getBitmap() {
769        return getBitmap(getWidth(), getHeight());
770    }
771
772    public Bitmap getBitmap(int width, int height) {
773        if (width == 0 || height == 0 || getWidth() == 0 || getHeight() == 0) return null;
774
775        Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
776
777        if (mNativeContentViewCore != 0 &&
778                nativePopulateBitmapFromCompositor(mNativeContentViewCore, b)) {
779            // If we successfully grabbed a bitmap, check if we have to draw the Android overlay
780            // components as well.
781            if (mContainerView.getChildCount() > 0) {
782                Canvas c = new Canvas(b);
783                c.scale(1.0f, -1.0f, width / 2.0f, height / 2.0f);
784                c.scale(width / (float) getWidth(), height / (float) getHeight());
785                mContainerView.draw(c);
786            }
787            return b;
788        }
789
790        return null;
791    }
792
793    /**
794     * Generates a bitmap of the content that is performance optimized based on capture time.
795     *
796     * <p>
797     * To have a consistent capture time across devices, we will scale down the captured bitmap
798     * where necessary to reduce the time to generate the bitmap.
799     *
800     * @param width The width of the content to be captured.
801     * @param height The height of the content to be captured.
802     * @return A pair of the generated bitmap, and the scale that needs to be applied to return the
803     *         bitmap to it's original size (i.e. if the bitmap is scaled down 50%, this
804     *         will be 2).
805     */
806    public Pair<Bitmap, Float> getScaledPerformanceOptimizedBitmap(int width, int height) {
807        float scale = 1f;
808        // On tablets, always scale down to MDPI for performance reasons.
809        if (DeviceUtils.isTablet(getContext())) {
810            scale = getContext().getResources().getDisplayMetrics().density;
811        }
812        return Pair.create(
813                getBitmap((int) (width / scale), (int) (height / scale)),
814                scale);
815    }
816
817    /**
818     * @return Whether the ContentView is covered by an overlay that is more than half
819     *         of it's surface. This is used to determine if we need to do a slow bitmap capture or
820     *         to show the ContentView without them.
821     */
822    public boolean hasLargeOverlay() {
823        // TODO(nileshagrawal): Implement this.
824        return false;
825    }
826
827    /**
828     * @return Whether the current WebContents has a previous navigation entry.
829     */
830    public boolean canGoBack() {
831        return mNativeContentViewCore != 0 && nativeCanGoBack(mNativeContentViewCore);
832    }
833
834    /**
835     * @return Whether the current WebContents has a navigation entry after the current one.
836     */
837    public boolean canGoForward() {
838        return mNativeContentViewCore != 0 && nativeCanGoForward(mNativeContentViewCore);
839    }
840
841    /**
842     * @param offset The offset into the navigation history.
843     * @return Whether we can move in history by given offset
844     */
845    public boolean canGoToOffset(int offset) {
846        return mNativeContentViewCore != 0 && nativeCanGoToOffset(mNativeContentViewCore, offset);
847    }
848
849    /**
850     * Navigates to the specified offset from the "current entry". Does nothing if the offset is out
851     * of bounds.
852     * @param offset The offset into the navigation history.
853     */
854    public void goToOffset(int offset) {
855        if (mNativeContentViewCore != 0) nativeGoToOffset(mNativeContentViewCore, offset);
856    }
857
858    /**
859     * Goes to the navigation entry before the current one.
860     */
861    public void goBack() {
862        if (mNativeContentViewCore != 0) nativeGoBack(mNativeContentViewCore);
863    }
864
865    /**
866     * Goes to the navigation entry following the current one.
867     */
868    public void goForward() {
869        if (mNativeContentViewCore != 0) nativeGoForward(mNativeContentViewCore);
870    }
871
872    /**
873     * Reload the current page.
874     */
875    public void reload() {
876        mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
877        if (mNativeContentViewCore != 0) nativeReload(mNativeContentViewCore);
878    }
879
880    /**
881     * Cancel the pending reload.
882     */
883    public void cancelPendingReload() {
884        if (mNativeContentViewCore != 0) nativeCancelPendingReload(mNativeContentViewCore);
885    }
886
887    /**
888     * Continue the pending reload.
889     */
890    public void continuePendingReload() {
891        if (mNativeContentViewCore != 0) nativeContinuePendingReload(mNativeContentViewCore);
892    }
893
894    /**
895     * Clears the ContentViewCore's page history in both the backwards and
896     * forwards directions.
897     */
898    public void clearHistory() {
899        if (mNativeContentViewCore != 0) nativeClearHistory(mNativeContentViewCore);
900    }
901
902    String getSelectedText() {
903        return mHasSelection ? mLastSelectedText : "";
904    }
905
906    // End FrameLayout overrides.
907
908    /**
909     * @see {@link android.webkit.WebView#flingScroll(int, int)}
910     */
911    public void flingScroll(int vx, int vy) {
912        // Notes:
913        //   (1) Use large negative values for the x/y parameters so we don't accidentally scroll a
914        //       nested frame.
915        //   (2) vx and vy are inverted to match WebView behavior.
916        mContentViewGestureHandler.fling(
917                System.currentTimeMillis(), -Integer.MAX_VALUE, -Integer.MIN_VALUE, -vx, -vy);
918    }
919
920    /**
921     * @see View#onTouchEvent(MotionEvent)
922     */
923    public boolean onTouchEvent(MotionEvent event) {
924        undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
925        return mContentViewGestureHandler.onTouchEvent(event);
926    }
927
928    /**
929     * @return ContentViewGestureHandler for all MotionEvent and gesture related calls.
930     */
931    ContentViewGestureHandler getContentViewGestureHandler() {
932        return mContentViewGestureHandler;
933    }
934
935    @Override
936    public boolean sendTouchEvent(long timeMs, int action, TouchPoint[] pts) {
937        if (mNativeContentViewCore != 0) {
938            return nativeSendTouchEvent(mNativeContentViewCore, timeMs, action, pts);
939        }
940        return false;
941    }
942
943    @SuppressWarnings("unused")
944    @CalledByNative
945    private void hasTouchEventHandlers(boolean hasTouchHandlers) {
946        mContentViewGestureHandler.hasTouchEventHandlers(hasTouchHandlers);
947    }
948
949    @SuppressWarnings("unused")
950    @CalledByNative
951    private void confirmTouchEvent(boolean handled) {
952        mContentViewGestureHandler.confirmTouchEvent(handled);
953    }
954
955    @Override
956    public boolean sendGesture(int type, long timeMs, int x, int y, Bundle b) {
957        if (mNativeContentViewCore == 0) return false;
958
959        switch (type) {
960            case ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE:
961                nativeShowPressState(mNativeContentViewCore, timeMs, x, y);
962                return true;
963            case ContentViewGestureHandler.GESTURE_DOUBLE_TAP:
964                nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
965                return true;
966            case ContentViewGestureHandler.GESTURE_SINGLE_TAP_UP:
967                nativeSingleTap(mNativeContentViewCore, timeMs, x, y, false);
968                return true;
969            case ContentViewGestureHandler.GESTURE_SINGLE_TAP_CONFIRMED:
970                handleTapOrPress(timeMs, x, y, false,
971                        b.getBoolean(ContentViewGestureHandler.SHOW_PRESS, false));
972                return true;
973            case ContentViewGestureHandler.GESTURE_LONG_PRESS:
974                handleTapOrPress(timeMs, x, y, true, false);
975                return true;
976            case ContentViewGestureHandler.GESTURE_SCROLL_START:
977                nativeScrollBegin(mNativeContentViewCore, timeMs, x, y);
978                return true;
979            case ContentViewGestureHandler.GESTURE_SCROLL_BY: {
980                int dx = b.getInt(ContentViewGestureHandler.DISTANCE_X);
981                int dy = b.getInt(ContentViewGestureHandler.DISTANCE_Y);
982                nativeScrollBy(mNativeContentViewCore, timeMs, x, y, dx, dy);
983                return true;
984            }
985            case ContentViewGestureHandler.GESTURE_SCROLL_END:
986                nativeScrollEnd(mNativeContentViewCore, timeMs);
987                return true;
988            case ContentViewGestureHandler.GESTURE_FLING_START:
989                nativeFlingStart(mNativeContentViewCore, timeMs, x, y,
990                        clampFlingVelocityX(b.getInt(ContentViewGestureHandler.VELOCITY_X, 0)),
991                        clampFlingVelocityY(b.getInt(ContentViewGestureHandler.VELOCITY_Y, 0)));
992                return true;
993            case ContentViewGestureHandler.GESTURE_FLING_CANCEL:
994                nativeFlingCancel(mNativeContentViewCore, timeMs);
995                return true;
996            case ContentViewGestureHandler.GESTURE_PINCH_BEGIN:
997                nativePinchBegin(mNativeContentViewCore, timeMs, x, y);
998                return true;
999            case ContentViewGestureHandler.GESTURE_PINCH_BY:
1000                nativePinchBy(mNativeContentViewCore, timeMs, x, y,
1001                        b.getFloat(ContentViewGestureHandler.DELTA, 0));
1002                return true;
1003            case ContentViewGestureHandler.GESTURE_PINCH_END:
1004                nativePinchEnd(mNativeContentViewCore, timeMs);
1005                return true;
1006            default:
1007                return false;
1008        }
1009    }
1010
1011    /**
1012     * Injects the passed JavaScript code in the current page and evaluates it.
1013     * Once evaluated, an asynchronous call to
1014     * ContentViewClient.onJavaScriptEvaluationResult is made. Used in automation
1015     * tests.
1016     *
1017     * @return an id that is passed along in the asynchronous onJavaScriptEvaluationResult callback
1018     * @throws IllegalStateException If the ContentView has been destroyed.
1019     */
1020    public int evaluateJavaScript(String script) throws IllegalStateException {
1021        checkIsAlive();
1022        return nativeEvaluateJavaScript(script);
1023    }
1024
1025    /**
1026     * This method should be called when the containing activity is paused.
1027     */
1028    public void onActivityPause() {
1029        TraceEvent.begin();
1030        hidePopupDialog();
1031        nativeOnHide(mNativeContentViewCore);
1032        setAccessibilityState(false);
1033        TraceEvent.end();
1034        mVSyncMonitor.stop();
1035    }
1036
1037    /**
1038     * This method should be called when the containing activity is resumed.
1039     */
1040    public void onActivityResume() {
1041        nativeOnShow(mNativeContentViewCore);
1042        setAccessibilityState(true);
1043    }
1044
1045    /**
1046     * To be called when the ContentView is shown.
1047     */
1048    public void onShow() {
1049        nativeOnShow(mNativeContentViewCore);
1050        setAccessibilityState(true);
1051    }
1052
1053    /**
1054     * To be called when the ContentView is hidden.
1055     */
1056    public void onHide() {
1057        hidePopupDialog();
1058        setAccessibilityState(false);
1059        nativeOnHide(mNativeContentViewCore);
1060        mVSyncMonitor.stop();
1061    }
1062
1063    /**
1064     * Return the ContentSettings object used to control the settings for this
1065     * ContentViewCore.
1066     *
1067     * Note that when ContentView is used in the PERSONALITY_CHROME role,
1068     * ContentSettings can only be used for retrieving settings values. For
1069     * modifications, ChromeNativePreferences is to be used.
1070     * @return A ContentSettings object that can be used to control this
1071     *         ContentViewCore's settings.
1072     */
1073    public ContentSettings getContentSettings() {
1074        return mContentSettings;
1075    }
1076
1077    @Override
1078    public boolean didUIStealScroll(float x, float y) {
1079        return getContentViewClient().shouldOverrideScroll(
1080                x, y, computeHorizontalScrollOffset(), computeVerticalScrollOffset());
1081    }
1082
1083    @Override
1084    public boolean hasFixedPageScale() {
1085        return mNativeMinimumScale == mNativeMaximumScale;
1086    }
1087
1088    private void hidePopupDialog() {
1089        SelectPopupDialog.hide(this);
1090        hideHandles();
1091        hideSelectActionBar();
1092    }
1093
1094    void hideSelectActionBar() {
1095        if (mActionMode != null) {
1096            mActionMode.finish();
1097        }
1098    }
1099
1100    /**
1101     * @see View#onAttachedToWindow()
1102     */
1103    @SuppressWarnings("javadoc")
1104    public void onAttachedToWindow() {
1105        setAccessibilityState(true);
1106    }
1107
1108    /**
1109     * @see View#onDetachedFromWindow()
1110     */
1111    @SuppressWarnings("javadoc")
1112    public void onDetachedFromWindow() {
1113        setAccessibilityState(false);
1114    }
1115
1116    /**
1117     * @see View#onVisibilityChanged(android.view.View, int)
1118     */
1119    public void onVisibilityChanged(View changedView, int visibility) {
1120      if (visibility != View.VISIBLE) {
1121          if (mContentSettings.supportZoom()) {
1122              mZoomManager.dismissZoomPicker();
1123          }
1124      }
1125    }
1126
1127    @CalledByNative
1128    private void onWebPreferencesUpdated() {
1129        mContentSettings.syncSettings();
1130    }
1131
1132    /**
1133     * @see View#onCreateInputConnection(EditorInfo)
1134     */
1135    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1136        if (!mImeAdapter.hasTextInputType()) {
1137            // Although onCheckIsTextEditor will return false in this case, the EditorInfo
1138            // is still used by the InputMethodService. Need to make sure the IME doesn't
1139            // enter fullscreen mode.
1140            outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
1141        }
1142        mInputConnection = ImeAdapter.AdapterInputConnection.getInstance(
1143                mContainerView, mImeAdapter, outAttrs);
1144        return mInputConnection;
1145    }
1146
1147    /**
1148     * @see View#onCheckIsTextEditor()
1149     */
1150    public boolean onCheckIsTextEditor() {
1151        return mImeAdapter.hasTextInputType();
1152    }
1153
1154    /**
1155     * @see View#onConfigurationChanged(Configuration)
1156     */
1157    @SuppressWarnings("javadoc")
1158    public void onConfigurationChanged(Configuration newConfig) {
1159        TraceEvent.begin();
1160
1161        mKeyboardConnected = newConfig.keyboard != Configuration.KEYBOARD_NOKEYS;
1162
1163        if (mKeyboardConnected) {
1164            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
1165                    ImeAdapter.sTextInputTypeNone);
1166            InputMethodManager manager = (InputMethodManager)
1167                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
1168            manager.restartInput(mContainerView);
1169        }
1170        mContainerViewInternals.super_onConfigurationChanged(newConfig);
1171        mNeedUpdateOrientationChanged = true;
1172        TraceEvent.end();
1173    }
1174
1175    /**
1176     * @see View#onSizeChanged(int, int, int, int)
1177     */
1178    @SuppressWarnings("javadoc")
1179    public void onSizeChanged(int w, int h, int ow, int oh) {
1180        mPopupZoomer.hide(false);
1181
1182        updateAfterSizeChanged();
1183
1184        // Update the content size to make sure it is at least the View size
1185        if (mContentWidth < w) mContentWidth = w;
1186        if (mContentHeight < h) mContentHeight = h;
1187
1188        if (mViewportWidth != w || mViewportHeight != h) {
1189            mViewportWidth = w;
1190            mViewportHeight = h;
1191            if (mNativeContentViewCore != 0) {
1192                nativeSetSize(mNativeContentViewCore, mViewportWidth, mViewportHeight);
1193            }
1194        }
1195    }
1196
1197    public void updateAfterSizeChanged() {
1198        // Execute a delayed form focus operation because the OSK was brought
1199        // up earlier.
1200        if (mFocusOnNextSizeChanged) {
1201            scrollFocusedEditableNodeIntoView();
1202            mFocusOnNextSizeChanged = false;
1203        } else if (mUnfocusOnNextSizeChanged) {
1204            undoScrollFocusedEditableNodeIntoViewIfNeeded(true);
1205            mUnfocusOnNextSizeChanged = false;
1206        }
1207
1208        if (mNeedUpdateOrientationChanged) {
1209            sendOrientationChangeEvent();
1210            mNeedUpdateOrientationChanged = false;
1211        }
1212    }
1213
1214    private void scrollFocusedEditableNodeIntoView() {
1215        if (mNativeContentViewCore != 0) {
1216            Runnable scrollTask = new Runnable() {
1217                @Override
1218                public void run() {
1219                    if (mNativeContentViewCore != 0) {
1220                        nativeScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1221                    }
1222                }
1223            };
1224
1225            scrollTask.run();
1226
1227            // The native side keeps track of whether the zoom and scroll actually occurred. It is
1228            // more efficient to do it this way and sometimes fire an unnecessary message rather
1229            // than synchronize with the renderer and always have an additional message.
1230            mScrolledAndZoomedFocusedEditableNode = true;
1231        }
1232    }
1233
1234    private void undoScrollFocusedEditableNodeIntoViewIfNeeded(boolean backButtonPressed) {
1235        // The only call to this function that matters is the first call after the
1236        // scrollFocusedEditableNodeIntoView function call.
1237        // If the first call to this function is a result of a back button press we want to undo the
1238        // preceding scroll. If the call is a result of some other action we don't want to perform
1239        // an undo.
1240        // All subsequent calls are ignored since only the scroll function sets
1241        // mScrolledAndZoomedFocusedEditableNode to true.
1242        if (mScrolledAndZoomedFocusedEditableNode && backButtonPressed &&
1243                mNativeContentViewCore != 0) {
1244            Runnable scrollTask = new Runnable() {
1245                @Override
1246                public void run() {
1247                    if (mNativeContentViewCore != 0) {
1248                        nativeUndoScrollFocusedEditableNodeIntoView(mNativeContentViewCore);
1249                    }
1250                }
1251            };
1252
1253            scrollTask.run();
1254        }
1255        mScrolledAndZoomedFocusedEditableNode = false;
1256    }
1257
1258    /**
1259     * @see View#onFocusedChanged(boolean, int, Rect)
1260     */
1261    @SuppressWarnings("javadoc")
1262    public void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
1263        if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
1264    }
1265
1266    /**
1267     * @see View#onKeyUp(int, KeyEvent)
1268     */
1269    public boolean onKeyUp(int keyCode, KeyEvent event) {
1270        if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) {
1271            mPopupZoomer.hide(true);
1272            return true;
1273        }
1274        return mContainerViewInternals.super_onKeyUp(keyCode, event);
1275    }
1276
1277    /**
1278     * @see View#dispatchKeyEventPreIme(KeyEvent)
1279     */
1280    public boolean dispatchKeyEventPreIme(KeyEvent event) {
1281        try {
1282            TraceEvent.begin();
1283            if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && mImeAdapter.isActive()) {
1284                mUnfocusOnNextSizeChanged = true;
1285            } else {
1286                undoScrollFocusedEditableNodeIntoViewIfNeeded(false);
1287            }
1288            mImeAdapter.dispatchKeyEventPreIme(event);
1289            return mContainerViewInternals.super_dispatchKeyEventPreIme(event);
1290        } finally {
1291            TraceEvent.end();
1292        }
1293    }
1294
1295    /**
1296     * @see View#dispatchKeyEvent(KeyEvent)
1297     */
1298    public boolean dispatchKeyEvent(KeyEvent event) {
1299        if (mImeAdapter != null &&
1300                !mImeAdapter.isNativeImeAdapterAttached() && mNativeContentViewCore != 0) {
1301            mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore));
1302        }
1303        // The key handling logic is kind of confusing here.
1304        // The purpose of shouldOverrideKeyEvent() is to filter out some keys that is critical
1305        // to browser function but useless in renderer process (for example, the back button),
1306        // so the browser can still respond to these keys in a timely manner when the renderer
1307        // process is busy/blocked/busted. mImeAdapter.dispatchKeyEvent() forwards the key event
1308        // to the renderer process. If mImeAdapter is bypassed or is not interested to the event,
1309        // fall back to the default dispatcher to propagate the event to sub-views.
1310        return (!getContentViewClient().shouldOverrideKeyEvent(event)
1311                && mImeAdapter.dispatchKeyEvent(event))
1312                || mContainerViewInternals.super_dispatchKeyEvent(event);
1313    }
1314
1315    /**
1316     * @see View#onHoverEvent(MotionEvent)
1317     * Mouse move events are sent on hover enter, hover move and hover exit.
1318     * They are sent on hover exit because sometimes it acts as both a hover
1319     * move and hover exit.
1320     */
1321    public boolean onHoverEvent(MotionEvent event) {
1322        TraceEvent.begin("onHoverEvent");
1323        mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1324        if (mNativeContentViewCore != 0) {
1325            nativeSendMouseMoveEvent(mNativeContentViewCore, event.getEventTime(),
1326                    (int) event.getX(), (int) event.getY());
1327        }
1328        TraceEvent.end("onHoverEvent");
1329        return true;
1330    }
1331
1332    /**
1333     * @see View#onGenericMotionEvent(MotionEvent)
1334     */
1335    public boolean onGenericMotionEvent(MotionEvent event) {
1336        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
1337            switch (event.getAction()) {
1338                case MotionEvent.ACTION_SCROLL:
1339                    nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
1340                            (int) event.getX(), (int) event.getY(),
1341                            event.getAxisValue(MotionEvent.AXIS_VSCROLL));
1342
1343                    mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
1344                    // Send a delayed onMouseMove event so that we end
1345                    // up hovering over the right position after the scroll.
1346                    final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event);
1347                    mFakeMouseMoveRunnable = new Runnable() {
1348                          @Override
1349                          public void run() {
1350                              onHoverEvent(eventFakeMouseMove);
1351                          }
1352                    };
1353                    mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
1354                    return true;
1355            }
1356        }
1357        return mContainerViewInternals.super_onGenericMotionEvent(event);
1358    }
1359
1360    /**
1361     * @see View#scrollBy(int, int)
1362     * Currently the ContentView scrolling happens in the native side. In
1363     * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
1364     * are overridden, so that View's mScrollX and mScrollY will be unchanged at
1365     * (0, 0). This is critical for drawing ContentView correctly.
1366     */
1367    public void scrollBy(int x, int y) {
1368        if (mNativeContentViewCore != 0) {
1369            nativeScrollBy(mNativeContentViewCore, System.currentTimeMillis(), 0, 0, x, y);
1370        }
1371    }
1372
1373    /**
1374     * @see View#scrollTo(int, int)
1375     */
1376    public void scrollTo(int x, int y) {
1377        if (mNativeContentViewCore == 0) return;
1378        int dx = x - mNativeScrollX, dy = y - mNativeScrollY;
1379        if (dx != 0 || dy != 0) {
1380            long time = System.currentTimeMillis();
1381            nativeScrollBegin(mNativeContentViewCore, time, mNativeScrollX, mNativeScrollY);
1382            nativeScrollBy(mNativeContentViewCore, time, mNativeScrollX, mNativeScrollY,
1383                    dx, dy);
1384            nativeScrollEnd(mNativeContentViewCore, time);
1385        }
1386    }
1387
1388    // NOTE: this can go away once ContentView.getScrollX() reports correct values.
1389    //       see: b/6029133
1390    public int getNativeScrollXForTest() {
1391        return mNativeScrollX;
1392    }
1393
1394    // NOTE: this can go away once ContentView.getScrollY() reports correct values.
1395    //       see: b/6029133
1396    public int getNativeScrollYForTest() {
1397        return mNativeScrollY;
1398    }
1399
1400    /**
1401     * @see View#computeHorizontalScrollExtent()
1402     */
1403    @SuppressWarnings("javadoc")
1404    public int computeHorizontalScrollExtent() {
1405        return getWidth();
1406    }
1407
1408    /**
1409     * @see View#computeHorizontalScrollOffset()
1410     */
1411    @SuppressWarnings("javadoc")
1412    public int computeHorizontalScrollOffset() {
1413        return mNativeScrollX;
1414    }
1415
1416    /**
1417     * @see View#computeHorizontalScrollRange()
1418     */
1419    @SuppressWarnings("javadoc")
1420    public int computeHorizontalScrollRange() {
1421        return mContentWidth;
1422    }
1423
1424    /**
1425     * @see View#computeVerticalScrollExtent()
1426     */
1427    @SuppressWarnings("javadoc")
1428    public int computeVerticalScrollExtent() {
1429        return getHeight();
1430    }
1431
1432    /**
1433     * @see View#computeVerticalScrollOffset()
1434     */
1435    @SuppressWarnings("javadoc")
1436    public int computeVerticalScrollOffset() {
1437        return mNativeScrollY;
1438    }
1439
1440    /**
1441     * @see View#computeVerticalScrollRange()
1442     */
1443    @SuppressWarnings("javadoc")
1444    public int computeVerticalScrollRange() {
1445        return mContentHeight;
1446    }
1447
1448    // End FrameLayout overrides.
1449
1450    /**
1451     * @see View#awakenScrollBars(int, boolean)
1452     */
1453    @SuppressWarnings("javadoc")
1454    public boolean awakenScrollBars(int startDelay, boolean invalidate) {
1455        // For the default implementation of ContentView which draws the scrollBars on the native
1456        // side, calling this function may get us into a bad state where we keep drawing the
1457        // scrollBars, so disable it by always returning false.
1458        if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
1459            return false;
1460        } else {
1461            return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate);
1462        }
1463    }
1464
1465    @SuppressWarnings("unused")
1466    @CalledByNative
1467    private void onTabCrash() {
1468        getContentViewClient().onTabCrash();
1469    }
1470
1471    private void handleTapOrPress(
1472            long timeMs, int x, int y, boolean isLongPress, boolean showPress) {
1473        if (!mContainerView.isFocused()) mContainerView.requestFocus();
1474
1475        if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(x, y);
1476
1477        if (isLongPress) {
1478            getInsertionHandleController().allowAutomaticShowing();
1479            getSelectionHandleController().allowAutomaticShowing();
1480            if (mNativeContentViewCore != 0) {
1481                nativeLongPress(mNativeContentViewCore, timeMs, x, y, false);
1482            }
1483        } else {
1484            if (!showPress && mNativeContentViewCore != 0) {
1485                nativeShowPressState(mNativeContentViewCore, timeMs, x, y);
1486            }
1487            if (mSelectionEditable) getInsertionHandleController().allowAutomaticShowing();
1488            if (mNativeContentViewCore != 0) {
1489                nativeSingleTap(mNativeContentViewCore, timeMs, x, y, false);
1490            }
1491        }
1492    }
1493
1494    void updateMultiTouchZoomSupport() {
1495        mZoomManager.updateMultiTouchSupport();
1496    }
1497
1498    public boolean isMultiTouchZoomSupported() {
1499        return mZoomManager.isMultiTouchZoomSupported();
1500    }
1501
1502    void selectPopupMenuItems(int[] indices) {
1503        if (mNativeContentViewCore != 0) {
1504            nativeSelectPopupMenuItems(mNativeContentViewCore, indices);
1505        }
1506    }
1507
1508    /*
1509     * To avoid checkerboard, we clamp the fling velocity based on the maximum number of tiles
1510     * allowed to be uploaded per 100ms. Calculation is limited to one direction. We assume the
1511     * tile size is 256x256. The precise distance / velocity should be calculated based on the
1512     * logic in Scroller.java. As it is almost linear for the first 100ms, we use a simple math.
1513     */
1514    private int clampFlingVelocityX(int velocity) {
1515        int cols = mMaxNumUploadTiles / (int) (Math.ceil((float) getHeight() / 256) + 1);
1516        int maxVelocity = cols > 0 ? cols * 2560 : 1000;
1517        if (Math.abs(velocity) > maxVelocity) {
1518            return velocity > 0 ? maxVelocity : -maxVelocity;
1519        } else {
1520            return velocity;
1521        }
1522    }
1523
1524    private int clampFlingVelocityY(int velocity) {
1525        int rows = mMaxNumUploadTiles / (int) (Math.ceil((float) getWidth() / 256) + 1);
1526        int maxVelocity = rows > 0 ? rows * 2560 : 1000;
1527        if (Math.abs(velocity) > maxVelocity) {
1528            return velocity > 0 ? maxVelocity : -maxVelocity;
1529        } else {
1530            return velocity;
1531        }
1532    }
1533
1534    /**
1535     * Get the screen orientation from the OS and push it to WebKit.
1536     *
1537     * TODO(husky): Add a hook for mock orientations.
1538     *
1539     * TODO(husky): Currently each new tab starts with an orientation of 0 until you actually
1540     * rotate the device. This is wrong if you actually started in landscape mode. To fix this, we
1541     * need to push the correct orientation, but only after WebKit's Frame object has been fully
1542     * initialized. Need to find a good time to do that. onPageFinished() would probably work but
1543     * it isn't implemented yet.
1544     */
1545    private void sendOrientationChangeEvent() {
1546        if (mNativeContentViewCore == 0) return;
1547
1548        WindowManager windowManager =
1549                (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
1550        switch (windowManager.getDefaultDisplay().getRotation()) {
1551            case Surface.ROTATION_90:
1552                nativeSendOrientationChangeEvent(mNativeContentViewCore, 90);
1553                break;
1554            case Surface.ROTATION_180:
1555                nativeSendOrientationChangeEvent(mNativeContentViewCore, 180);
1556                break;
1557            case Surface.ROTATION_270:
1558                nativeSendOrientationChangeEvent(mNativeContentViewCore, -90);
1559                break;
1560            case Surface.ROTATION_0:
1561                nativeSendOrientationChangeEvent(mNativeContentViewCore, 0);
1562                break;
1563            default:
1564                Log.w(TAG, "Unknown rotation!");
1565                break;
1566        }
1567    }
1568
1569    /**
1570     * Register the delegate to be used when content can not be handled by
1571     * the rendering engine, and should be downloaded instead. This will replace
1572     * the current delegate, if any.
1573     * @param delegate An implementation of ContentViewDownloadDelegate.
1574     */
1575    public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
1576        mDownloadDelegate = delegate;
1577    }
1578
1579    // Called by DownloadController.
1580    ContentViewDownloadDelegate getDownloadDelegate() {
1581        return mDownloadDelegate;
1582    }
1583
1584    private SelectionHandleController getSelectionHandleController() {
1585        if (mSelectionHandleController == null) {
1586            mSelectionHandleController = new SelectionHandleController(getContainerView()) {
1587                @Override
1588                public void selectBetweenCoordinates(int x1, int y1, int x2, int y2) {
1589                    if (mNativeContentViewCore != 0 && !(x1 == x2 && y1 == y2)) {
1590                        nativeSelectBetweenCoordinates(mNativeContentViewCore, x1, y1, x2, y2);
1591                    }
1592                }
1593
1594                @Override
1595                public void showHandlesAt(int x1, int y1, int dir1, int x2, int y2, int dir2) {
1596                    super.showHandlesAt(x1, y1, dir1, x2, y2, dir2);
1597                    mStartHandleNormalizedPoint.set(
1598                        (x1 + mNativeScrollX) / mNativePageScaleFactor,
1599                        (y1 + mNativeScrollY) / mNativePageScaleFactor);
1600                    mEndHandleNormalizedPoint.set(
1601                        (x2 + mNativeScrollX) / mNativePageScaleFactor,
1602                        (y2 + mNativeScrollY) / mNativePageScaleFactor);
1603
1604                    showSelectActionBar();
1605                }
1606
1607            };
1608
1609            mSelectionHandleController.hideAndDisallowAutomaticShowing();
1610        }
1611
1612        return mSelectionHandleController;
1613    }
1614
1615    private InsertionHandleController getInsertionHandleController() {
1616        if (mInsertionHandleController == null) {
1617            mInsertionHandleController = new InsertionHandleController(getContainerView()) {
1618                private static final int AVERAGE_LINE_HEIGHT = 14;
1619
1620                @Override
1621                public void setCursorPosition(int x, int y) {
1622                    if (mNativeContentViewCore != 0) {
1623                        nativeSelectBetweenCoordinates(mNativeContentViewCore, x, y, x, y);
1624                    }
1625                }
1626
1627                @Override
1628                public void paste() {
1629                    mImeAdapter.paste();
1630                    hideHandles();
1631                }
1632
1633                @Override
1634                public int getLineHeight() {
1635                    return (int) (mNativePageScaleFactor * AVERAGE_LINE_HEIGHT);
1636                }
1637
1638                @Override
1639                public void showHandleAt(int x, int y) {
1640                    super.showHandleAt(x, y);
1641                    mInsertionHandleNormalizedPoint.set(
1642                        (x + mNativeScrollX) / mNativePageScaleFactor,
1643                        (y + mNativeScrollY) / mNativePageScaleFactor);
1644                }
1645            };
1646
1647            mInsertionHandleController.hideAndDisallowAutomaticShowing();
1648        }
1649
1650        return mInsertionHandleController;
1651    }
1652
1653    private void updateHandleScreenPositions() {
1654        if (mSelectionHandleController != null && mSelectionHandleController.isShowing()) {
1655            float startX = mStartHandleNormalizedPoint.x * mNativePageScaleFactor - mNativeScrollX;
1656            float startY = mStartHandleNormalizedPoint.y * mNativePageScaleFactor - mNativeScrollY;
1657            mSelectionHandleController.setStartHandlePosition((int) startX, (int) startY);
1658
1659            float endX = mEndHandleNormalizedPoint.x * mNativePageScaleFactor - mNativeScrollX;
1660            float endY = mEndHandleNormalizedPoint.y * mNativePageScaleFactor - mNativeScrollY;
1661            mSelectionHandleController.setEndHandlePosition((int) endX, (int) endY);
1662        }
1663
1664        if (mInsertionHandleController != null && mInsertionHandleController.isShowing()) {
1665            float x = mInsertionHandleNormalizedPoint.x * mNativePageScaleFactor - mNativeScrollX;
1666            float y = mInsertionHandleNormalizedPoint.y * mNativePageScaleFactor - mNativeScrollY;
1667            mInsertionHandleController.setHandlePosition((int) x, (int) y);
1668        }
1669    }
1670
1671    private void hideHandles() {
1672        if (mSelectionHandleController != null) {
1673            mSelectionHandleController.hideAndDisallowAutomaticShowing();
1674        }
1675        if (mInsertionHandleController != null) {
1676            mInsertionHandleController.hideAndDisallowAutomaticShowing();
1677        }
1678    }
1679
1680    private void showSelectActionBar() {
1681        if (mActionMode != null) {
1682            mActionMode.invalidate();
1683            return;
1684        }
1685
1686        // Start a new action mode with a SelectActionModeCallback.
1687        SelectActionModeCallback.ActionHandler actionHandler =
1688                new SelectActionModeCallback.ActionHandler() {
1689            @Override
1690            public boolean selectAll() {
1691                return mImeAdapter.selectAll();
1692            }
1693
1694            @Override
1695            public boolean cut() {
1696                return mImeAdapter.cut();
1697            }
1698
1699            @Override
1700            public boolean copy() {
1701                return mImeAdapter.copy();
1702            }
1703
1704            @Override
1705            public boolean paste() {
1706                return mImeAdapter.paste();
1707            }
1708
1709            @Override
1710            public boolean isSelectionEditable() {
1711                return mSelectionEditable;
1712            }
1713
1714            @Override
1715            public String getSelectedText() {
1716                return ContentViewCore.this.getSelectedText();
1717            }
1718
1719            @Override
1720            public void onDestroyActionMode() {
1721                mActionMode = null;
1722                mImeAdapter.unselect();
1723                getContentViewClient().onContextualActionBarHidden();
1724            }
1725        };
1726        mActionMode = mContainerView.startActionMode(
1727                getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler,
1728                        nativeIsIncognito(mNativeContentViewCore)));
1729        if (mActionMode == null) {
1730            // There is no ActionMode, so remove the selection.
1731            mImeAdapter.unselect();
1732        } else {
1733            getContentViewClient().onContextualActionBarShown();
1734        }
1735    }
1736
1737    public boolean getUseDesktopUserAgent() {
1738        if (mNativeContentViewCore != 0) {
1739            return nativeGetUseDesktopUserAgent(mNativeContentViewCore);
1740        }
1741        return false;
1742    }
1743
1744    /**
1745     * Set whether or not we're using a desktop user agent for the currently loaded page.
1746     * @param override If true, use a desktop user agent.  Use a mobile one otherwise.
1747     * @param reloadOnChange Reload the page if the UA has changed.
1748     */
1749    public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange) {
1750        if (mNativeContentViewCore != 0) {
1751            nativeSetUseDesktopUserAgent(mNativeContentViewCore, override, reloadOnChange);
1752        }
1753    }
1754
1755    public void clearSslPreferences() {
1756        nativeClearSslPreferences(mNativeContentViewCore);
1757    }
1758
1759    /**
1760     * @return Whether the native ContentView has crashed.
1761     */
1762    public boolean isCrashed() {
1763        if (mNativeContentViewCore == 0) return false;
1764        return nativeCrashed(mNativeContentViewCore);
1765    }
1766
1767    @SuppressWarnings("unused")
1768    @CalledByNative
1769    private void updateContentSize(int width, int height) {
1770        if (mContentWidth != width || mContentHeight != height) {
1771            mPopupZoomer.hide(true);
1772        }
1773        // Make sure the content size is at least the View size
1774        mContentWidth = Math.max(width, getWidth());
1775        mContentHeight = Math.max(height, getHeight());
1776    }
1777
1778    @SuppressWarnings("unused")
1779    @CalledByNative
1780    private void updateScrollOffsetAndPageScaleFactor(int x, int y, float scale) {
1781        if (mNativeScrollX == x && mNativeScrollY == y && mNativePageScaleFactor == scale) return;
1782
1783        mContainerViewInternals.onScrollChanged(x, y, mNativeScrollX, mNativeScrollY);
1784
1785        // This function should be called back from native as soon
1786        // as the scroll is applied to the backbuffer.  We should only
1787        // update mNativeScrollX/Y here for consistency.
1788        mNativeScrollX = x;
1789        mNativeScrollY = y;
1790        mNativePageScaleFactor = scale;
1791
1792        mPopupZoomer.hide(true);
1793        updateHandleScreenPositions();
1794
1795        mZoomManager.updateZoomControls();
1796    }
1797
1798    @SuppressWarnings("unused")
1799    @CalledByNative
1800    private void updatePageScaleLimits(float minimumScale, float maximumScale) {
1801        mNativeMinimumScale = minimumScale;
1802        mNativeMaximumScale = maximumScale;
1803        mZoomManager.updateZoomControls();
1804    }
1805
1806    @SuppressWarnings("unused")
1807    @CalledByNative
1808    private void imeUpdateAdapter(int nativeImeAdapterAndroid, int textInputType,
1809            String text, int selectionStart, int selectionEnd,
1810            int compositionStart, int compositionEnd, boolean showImeIfNeeded) {
1811        TraceEvent.begin();
1812
1813        // Non-breaking spaces can cause the IME to get confused. Replace with normal spaces.
1814        text = text.replace('\u00A0', ' ');
1815
1816        mSelectionEditable = (textInputType != ImeAdapter.sTextInputTypeNone);
1817
1818        if (mActionMode != null) mActionMode.invalidate();
1819
1820        mImeAdapter.attachAndShowIfNeeded(nativeImeAdapterAndroid, textInputType,
1821                text, showImeIfNeeded);
1822
1823        if (mInputConnection != null) {
1824            // In WebKit if there's a composition then the selection will usually be the
1825            // same as the composition, whereas Android IMEs expect the selection to be
1826            // just a caret at the end of the composition.
1827            if (selectionStart == compositionStart && selectionEnd == compositionEnd) {
1828                selectionStart = selectionEnd;
1829            }
1830            mInputConnection.setEditableText(text, selectionStart, selectionEnd,
1831                    compositionStart, compositionEnd);
1832        }
1833        TraceEvent.end();
1834    }
1835
1836    @SuppressWarnings("unused")
1837    @CalledByNative
1838    private void setTitle(String title) {
1839        getContentViewClient().onUpdateTitle(title);
1840    }
1841
1842    /**
1843     * Called (from native) when the <select> popup needs to be shown.
1844     * @param items           Items to show.
1845     * @param enabled         POPUP_ITEM_TYPEs for items.
1846     * @param multiple        Whether the popup menu should support multi-select.
1847     * @param selectedIndices Indices of selected items.
1848     */
1849    @SuppressWarnings("unused")
1850    @CalledByNative
1851    private void showSelectPopup(String[] items, int[] enabled, boolean multiple,
1852            int[] selectedIndices) {
1853        SelectPopupDialog.show(this, items, enabled, multiple, selectedIndices);
1854    }
1855
1856    @SuppressWarnings("unused")
1857    @CalledByNative
1858    private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) {
1859        mPopupZoomer.setBitmap(zoomedBitmap);
1860        mPopupZoomer.show(targetRect);
1861    }
1862
1863    @SuppressWarnings("unused")
1864    @CalledByNative
1865    private void onSelectionChanged(String text) {
1866        mLastSelectedText = text;
1867    }
1868
1869    @SuppressWarnings("unused")
1870    @CalledByNative
1871    private void onSelectionBoundsChanged(Rect startRect, int dir1, Rect endRect, int dir2) {
1872        int x1 = startRect.left;
1873        int y1 = startRect.bottom;
1874        int x2 = endRect.left;
1875        int y2 = endRect.bottom;
1876        if (x1 != x2 || y1 != y2) {
1877            if (mInsertionHandleController != null) {
1878                mInsertionHandleController.hide();
1879            }
1880            getSelectionHandleController().onSelectionChanged(x1, y1, dir1, x2, y2, dir2);
1881            mHasSelection = true;
1882        } else {
1883            hideSelectActionBar();
1884            if (x1 != 0 && y1 != 0
1885                    && (mSelectionHandleController == null
1886                            || !mSelectionHandleController.isDragging())
1887                    && mSelectionEditable) {
1888                // Selection is a caret, and a text field is focused.
1889                if (mSelectionHandleController != null) {
1890                    mSelectionHandleController.hide();
1891                }
1892                getInsertionHandleController().onCursorPositionChanged(x1, y1);
1893                InputMethodManager manager = (InputMethodManager)
1894                        getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
1895                if (manager.isWatchingCursor(mContainerView)) {
1896                    manager.updateCursor(mContainerView, startRect.left, startRect.top,
1897                            startRect.right, startRect.bottom);
1898                }
1899            } else {
1900                // Deselection
1901                if (mSelectionHandleController != null
1902                        && !mSelectionHandleController.isDragging()) {
1903                    mSelectionHandleController.hideAndDisallowAutomaticShowing();
1904                }
1905                if (mInsertionHandleController != null) {
1906                    mInsertionHandleController.hideAndDisallowAutomaticShowing();
1907                }
1908            }
1909            mHasSelection = false;
1910        }
1911    }
1912
1913    @SuppressWarnings("unused")
1914    @CalledByNative
1915    private void onEvaluateJavaScriptResult(int id, String jsonResult) {
1916        getContentViewClient().onEvaluateJavaScriptResult(id, jsonResult);
1917    }
1918
1919    @SuppressWarnings("unused")
1920    @CalledByNative
1921    private void showPastePopup(int x, int y) {
1922        getInsertionHandleController()
1923                .showHandleWithPastePopupAt(x - mNativeScrollX, y - mNativeScrollY);
1924    }
1925
1926    /**
1927     * @return Whether a reload happens when this ContentView is activated.
1928     */
1929    public boolean needsReload() {
1930        return mNativeContentViewCore != 0 && nativeNeedsReload(mNativeContentViewCore);
1931    }
1932
1933    /**
1934     * @see View#hasFocus()
1935     */
1936    @CalledByNative
1937    public boolean hasFocus() {
1938        return mContainerView.hasFocus();
1939    }
1940
1941    /**
1942     * Checks whether the ContentViewCore can be zoomed in.
1943     *
1944     * @return True if the ContentViewCore can be zoomed in.
1945     */
1946    // This method uses the term 'zoom' for legacy reasons, but relates
1947    // to what chrome calls the 'page scale factor'.
1948    public boolean canZoomIn() {
1949        return mNativeMaximumScale - mNativePageScaleFactor > ZOOM_CONTROLS_EPSILON;
1950    }
1951
1952    /**
1953     * Checks whether the ContentViewCore can be zoomed out.
1954     *
1955     * @return True if the ContentViewCore can be zoomed out.
1956     */
1957    // This method uses the term 'zoom' for legacy reasons, but relates
1958    // to what chrome calls the 'page scale factor'.
1959    public boolean canZoomOut() {
1960        return mNativePageScaleFactor - mNativeMinimumScale > ZOOM_CONTROLS_EPSILON;
1961    }
1962
1963    /**
1964     * Zooms in the ContentViewCore by 25% (or less if that would result in
1965     * zooming in more than possible).
1966     *
1967     * @return True if there was a zoom change, false otherwise.
1968     */
1969    // This method uses the term 'zoom' for legacy reasons, but relates
1970    // to what chrome calls the 'page scale factor'.
1971    public boolean zoomIn() {
1972        if (!canZoomIn()) {
1973            return false;
1974        }
1975        return zoomByDelta(1.25f);
1976    }
1977
1978    /**
1979     * Zooms out the ContentViewCore by 20% (or less if that would result in
1980     * zooming out more than possible).
1981     *
1982     * @return True if there was a zoom change, false otherwise.
1983     */
1984    // This method uses the term 'zoom' for legacy reasons, but relates
1985    // to what chrome calls the 'page scale factor'.
1986    public boolean zoomOut() {
1987        if (!canZoomOut()) {
1988            return false;
1989        }
1990        return zoomByDelta(0.8f);
1991    }
1992
1993    /**
1994     * Resets the zoom factor of the ContentViewCore.
1995     *
1996     * @return True if there was a zoom change, false otherwise.
1997     */
1998    // This method uses the term 'zoom' for legacy reasons, but relates
1999    // to what chrome calls the 'page scale factor'.
2000    public boolean zoomReset() {
2001        // The page scale factor is initialized to mNativeMinimumScale when
2002        // the page finishes loading. Thus sets it back to mNativeMinimumScale.
2003        if (mNativePageScaleFactor - mNativeMinimumScale < ZOOM_CONTROLS_EPSILON) {
2004            return false;
2005        }
2006        return zoomByDelta(mNativeMinimumScale / mNativePageScaleFactor);
2007    }
2008
2009    private boolean zoomByDelta(float delta) {
2010        if (mNativeContentViewCore == 0) {
2011            return false;
2012        }
2013
2014        long timeMs = System.currentTimeMillis();
2015        int x = getWidth() / 2;
2016        int y = getHeight() / 2;
2017
2018        getContentViewGestureHandler().pinchBegin(timeMs, x, y);
2019        getContentViewGestureHandler().pinchBy(timeMs, x, y, delta);
2020        getContentViewGestureHandler().pinchEnd(timeMs);
2021
2022        return true;
2023    }
2024
2025    /**
2026     * Invokes the graphical zoom picker widget for this ContentView.
2027     */
2028    @Override
2029    public void invokeZoomPicker() {
2030        if (mContentSettings != null && mContentSettings.supportZoom()) {
2031            mZoomManager.invokeZoomPicker();
2032        }
2033    }
2034
2035    // Unlike legacy WebView getZoomControls which returns external zoom controls,
2036    // this method returns built-in zoom controls. This method is used in tests.
2037    public View getZoomControlsForTest() {
2038        return mZoomManager.getZoomControlsViewForTest();
2039    }
2040
2041    /**
2042     * This method injects the supplied Java object into the ContentViewCore.
2043     * The object is injected into the JavaScript context of the main frame,
2044     * using the supplied name. This allows the Java object to be accessed from
2045     * JavaScript. Note that that injected objects will not appear in
2046     * JavaScript until the page is next (re)loaded. For example:
2047     * <pre> view.addJavascriptInterface(new Object(), "injectedObject");
2048     * view.loadData("<!DOCTYPE html><title></title>", "text/html", null);
2049     * view.loadUrl("javascript:alert(injectedObject.toString())");</pre>
2050     * <p><strong>IMPORTANT:</strong>
2051     * <ul>
2052     * <li> addJavascriptInterface() can be used to allow JavaScript to control
2053     * the host application. This is a powerful feature, but also presents a
2054     * security risk. Use of this method in a ContentViewCore containing
2055     * untrusted content could allow an attacker to manipulate the host
2056     * application in unintended ways, executing Java code with the permissions
2057     * of the host application. Use extreme care when using this method in a
2058     * ContentViewCore which could contain untrusted content. Particular care
2059     * should be taken to avoid unintentional access to inherited methods, such
2060     * as {@link Object#getClass()}. To prevent access to inherited methods,
2061     * set {@code allowInheritedMethods} to {@code false}. In addition, ensure
2062     * that the injected object's public methods return only objects designed
2063     * to be used by untrusted code, and never return a raw Object instance.
2064     * <li> JavaScript interacts with Java objects on a private, background
2065     * thread of the ContentViewCore. Care is therefore required to maintain
2066     * thread safety.</li>
2067     * </ul></p>
2068     *
2069     * @param object The Java object to inject into the ContentViewCore's
2070     *               JavaScript context. Null values are ignored.
2071     * @param name The name used to expose the instance in JavaScript.
2072     * @param requireAnnotation Restrict exposed methods to ones with the
2073     *                          {@link JavascriptInterface} annotation.
2074     */
2075    public void addJavascriptInterface(Object object, String name, boolean requireAnnotation) {
2076        if (mNativeContentViewCore != 0 && object != null) {
2077            nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requireAnnotation);
2078        }
2079    }
2080
2081    /**
2082     * Removes a previously added JavaScript interface with the given name.
2083     *
2084     * @param name The name of the interface to remove.
2085     */
2086    public void removeJavascriptInterface(String name) {
2087        if (mNativeContentViewCore != 0) {
2088            nativeRemoveJavascriptInterface(mNativeContentViewCore, name);
2089        }
2090    }
2091
2092    /**
2093     * Return the current scale of the ContentView.
2094     * @return The current scale.
2095     */
2096    public float getScale() {
2097        return mNativePageScaleFactor;
2098    }
2099
2100    /**
2101     * If the view is ready to draw contents to the screen. In hardware mode,
2102     * the initialization of the surface texture may not occur until after the
2103     * view has been added to the layout. This method will return {@code true}
2104     * once the texture is actually ready.
2105     */
2106    public boolean isReady() {
2107        // TODO(nileshagrawal): Implement this.
2108        return false;
2109    }
2110
2111    /**
2112     * @return Whether or not the texture view is available or not.
2113     */
2114    public boolean isAvailable() {
2115        // TODO(nileshagrawal): Implement this.
2116        return false;
2117    }
2118
2119    @CalledByNative
2120    private void startContentIntent(String contentUrl) {
2121        getContentViewClient().onStartContentIntent(getContext(), contentUrl);
2122    }
2123
2124    /**
2125     * Determines whether or not this ContentViewCore can handle this accessibility action.
2126     * @param action The action to perform.
2127     * @return Whether or not this action is supported.
2128     */
2129    public boolean supportsAccessibilityAction(int action) {
2130        return mAccessibilityInjector.supportsAccessibilityAction(action);
2131    }
2132
2133    /**
2134     * Attempts to perform an accessibility action on the web content.  If the accessibility action
2135     * cannot be processed, it returns {@code null}, allowing the caller to know to call the
2136     * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value.
2137     * Otherwise the return value from this method should be used.
2138     * @param action The action to perform.
2139     * @param arguments Optional action arguments.
2140     * @return Whether the action was performed or {@code null} if the call should be delegated to
2141     *         the super {@link View} class.
2142     */
2143    public boolean performAccessibilityAction(int action, Bundle arguments) {
2144        if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
2145            return mAccessibilityInjector.performAccessibilityAction(action, arguments);
2146        }
2147
2148        return false;
2149    }
2150
2151    /**
2152     * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
2153     */
2154    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2155        mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
2156    }
2157
2158    /**
2159     * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
2160     */
2161    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
2162        event.setClassName(this.getClass().getName());
2163
2164        // Identify where the top-left of the screen currently points to.
2165        event.setScrollX(mNativeScrollX);
2166        event.setScrollY(mNativeScrollY);
2167
2168        // The maximum scroll values are determined by taking the content dimensions and
2169        // subtracting off the actual dimensions of the ChromeView.
2170        int maxScrollX = Math.max(0, mContentWidth - getWidth());
2171        int maxScrollY = Math.max(0, mContentHeight - getHeight());
2172        event.setScrollable(maxScrollX > 0 || maxScrollY > 0);
2173
2174        // Setting the maximum scroll values requires API level 15 or higher.
2175        final int SDK_VERSION_REQUIRED_TO_SET_SCROLL = 15;
2176        if (Build.VERSION.SDK_INT >= SDK_VERSION_REQUIRED_TO_SET_SCROLL) {
2177            event.setMaxScrollX(maxScrollX);
2178            event.setMaxScrollY(maxScrollY);
2179        }
2180    }
2181
2182    /**
2183     * Returns whether or not accessibility injection is being used.
2184     */
2185    public boolean isInjectingAccessibilityScript() {
2186        return mAccessibilityInjector.accessibilityIsAvailable();
2187    }
2188
2189    /**
2190     * Enable or disable accessibility features.
2191     */
2192    public void setAccessibilityState(boolean state) {
2193        mAccessibilityInjector.setScriptEnabled(state);
2194    }
2195
2196    /**
2197     * Stop any TTS notifications that are currently going on.
2198     */
2199    public void stopCurrentAccessibilityNotifications() {
2200        mAccessibilityInjector.onPageLostFocus();
2201    }
2202
2203    /**
2204     * @See android.webkit.WebView#pageDown(boolean)
2205     */
2206    public boolean pageDown(boolean bottom) {
2207        if (computeVerticalScrollOffset() >= mContentHeight - getHeight()) {
2208            // We seem to already be at the bottom of the page, so no scrolling will occur.
2209            return false;
2210        }
2211
2212        if (bottom) {
2213            scrollTo(computeHorizontalScrollOffset(), mContentHeight - getHeight());
2214        } else {
2215            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_DOWN));
2216            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_PAGE_DOWN));
2217        }
2218        return true;
2219    }
2220
2221    /**
2222     * @See android.webkit.WebView#pageUp(boolean)
2223     */
2224    public boolean pageUp(boolean top) {
2225        if (computeVerticalScrollOffset() == 0) {
2226            // We seem to already be at the top of the page, so no scrolling will occur.
2227            return false;
2228        }
2229
2230        if (top) {
2231            scrollTo(computeHorizontalScrollOffset(), 0);
2232        } else {
2233            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_UP));
2234            dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_PAGE_UP));
2235        }
2236        return true;
2237    }
2238
2239    /**
2240     * Callback factory method for nativeGetNavigationHistory().
2241     */
2242    @CalledByNative
2243    private void addToNavigationHistory(Object history, String url, String virtualUrl,
2244            String originalUrl, String title, Bitmap favicon) {
2245        NavigationEntry entry = new NavigationEntry(url, virtualUrl, originalUrl, title, favicon);
2246        ((NavigationHistory) history).addEntry(entry);
2247    }
2248
2249    /**
2250     * Get a copy of the navigation history of the view.
2251     */
2252    public NavigationHistory getNavigationHistory() {
2253        NavigationHistory history = new NavigationHistory();
2254        int currentIndex = nativeGetNavigationHistory(mNativeContentViewCore, history);
2255        history.setCurrentEntryIndex(currentIndex);
2256        return history;
2257    }
2258
2259    /**
2260     * Update of the latest vsync parameters.
2261     * @param tickTimeMicros The latest vsync tick time in microseconds.
2262     * @param intervalMicros The vsync interval in microseconds.
2263     */
2264    public void updateVSync(long tickTimeMicros, long intervalMicros) {
2265        if (mNativeContentViewCore != 0) {
2266            nativeUpdateVSyncParameters(mNativeContentViewCore, tickTimeMicros, intervalMicros);
2267        }
2268    }
2269
2270    /**
2271     *  Temporary shim for updateVSync.
2272     */
2273    public void UpdateVSync(long tickTimeMicros, long intervalMicros) {
2274        updateVSync(tickTimeMicros, intervalMicros);
2275    }
2276
2277    private native int nativeInit(boolean hardwareAccelerated, int webContentsPtr,
2278            int windowAndroidPtr);
2279
2280    private native void nativeOnJavaContentViewCoreDestroyed(int nativeContentViewCoreImpl);
2281
2282    private native void nativeLoadUrl(
2283            int nativeContentViewCoreImpl,
2284            String url,
2285            int loadUrlType,
2286            int transitionType,
2287            int uaOverrideOption,
2288            String extraHeaders,
2289            byte[] postData,
2290            String baseUrlForDataUrl,
2291            String virtualUrlForDataUrl,
2292            boolean canLoadLocalResources);
2293
2294    private native void nativeSetAllUserAgentOverridesInHistory(int nativeContentViewCoreImpl,
2295            String userAgentOverride);
2296
2297    private native String nativeGetURL(int nativeContentViewCoreImpl);
2298
2299    private native String nativeGetTitle(int nativeContentViewCoreImpl);
2300
2301    private native boolean nativeIsIncognito(int nativeContentViewCoreImpl);
2302
2303    // Returns true if the native side crashed so that java side can draw a sad tab.
2304    private native boolean nativeCrashed(int nativeContentViewCoreImpl);
2305
2306    private native void nativeSetFocus(int nativeContentViewCoreImpl, boolean focused);
2307
2308    private native void nativeSendOrientationChangeEvent(
2309            int nativeContentViewCoreImpl, int orientation);
2310
2311    private native boolean nativeSendTouchEvent(
2312            int nativeContentViewCoreImpl, long timeMs, int action, TouchPoint[] pts);
2313
2314    private native int nativeSendMouseMoveEvent(
2315            int nativeContentViewCoreImpl, long timeMs, int x, int y);
2316
2317    private native int nativeSendMouseWheelEvent(
2318            int nativeContentViewCoreImpl, long timeMs, int x, int y, float verticalAxis);
2319
2320    private native void nativeScrollBegin(int nativeContentViewCoreImpl, long timeMs, int x, int y);
2321
2322    private native void nativeScrollEnd(int nativeContentViewCoreImpl, long timeMs);
2323
2324    private native void nativeScrollBy(
2325            int nativeContentViewCoreImpl, long timeMs, int x, int y, int deltaX, int deltaY);
2326
2327    private native void nativeFlingStart(
2328            int nativeContentViewCoreImpl, long timeMs, int x, int y, int vx, int vy);
2329
2330    private native void nativeFlingCancel(int nativeContentViewCoreImpl, long timeMs);
2331
2332    private native void nativeSingleTap(
2333            int nativeContentViewCoreImpl, long timeMs, int x, int y, boolean linkPreviewTap);
2334
2335    private native void nativeShowPressState(
2336            int nativeContentViewCoreImpl, long timeMs, int x, int y);
2337
2338    private native void nativeDoubleTap(int nativeContentViewCoreImpl, long timeMs, int x, int y);
2339
2340    private native void nativeLongPress(int nativeContentViewCoreImpl, long timeMs, int x, int y,
2341            boolean linkPreviewTap);
2342
2343    private native void nativePinchBegin(int nativeContentViewCoreImpl, long timeMs, int x, int y);
2344
2345    private native void nativePinchEnd(int nativeContentViewCoreImpl, long timeMs);
2346
2347    private native void nativePinchBy(int nativeContentViewCoreImpl, long timeMs,
2348            int anchorX, int anchorY, float deltaScale);
2349
2350    private native void nativeSelectBetweenCoordinates(
2351            int nativeContentViewCoreImpl, int x1, int y1, int x2, int y2);
2352
2353    private native boolean nativeCanGoBack(int nativeContentViewCoreImpl);
2354
2355    private native boolean nativeCanGoForward(int nativeContentViewCoreImpl);
2356
2357    private native boolean nativeCanGoToOffset(int nativeContentViewCoreImpl, int offset);
2358
2359    private native void nativeGoToOffset(int nativeContentViewCoreImpl, int offset);
2360
2361    private native void nativeGoBack(int nativeContentViewCoreImpl);
2362
2363    private native void nativeGoForward(int nativeContentViewCoreImpl);
2364
2365    private native void nativeStopLoading(int nativeContentViewCoreImpl);
2366
2367    private native void nativeReload(int nativeContentViewCoreImpl);
2368
2369    private native void nativeCancelPendingReload(int nativeContentViewCoreImpl);
2370
2371    private native void nativeContinuePendingReload(int nativeContentViewCoreImpl);
2372
2373    private native void nativeSelectPopupMenuItems(int nativeContentViewCoreImpl, int[] indices);
2374
2375    private native void nativeScrollFocusedEditableNodeIntoView(int nativeContentViewCoreImpl);
2376    private native void nativeUndoScrollFocusedEditableNodeIntoView(int nativeContentViewCoreImpl);
2377    private native boolean nativeNeedsReload(int nativeContentViewCoreImpl);
2378
2379    private native void nativeClearHistory(int nativeContentViewCoreImpl);
2380
2381    private native int nativeEvaluateJavaScript(String script);
2382
2383    private native int nativeGetNativeImeAdapter(int nativeContentViewCoreImpl);
2384
2385    private native int nativeGetCurrentRenderProcessId(int nativeContentViewCoreImpl);
2386
2387    private native int nativeGetBackgroundColor(int nativeContentViewCoreImpl);
2388
2389    private native void nativeSetBackgroundColor(int nativeContentViewCoreImpl, int color);
2390
2391    private native void nativeOnShow(int nativeContentViewCoreImpl);
2392    private native void nativeOnHide(int nativeContentViewCoreImpl);
2393
2394    private native void nativeSetUseDesktopUserAgent(int nativeContentViewCoreImpl,
2395            boolean enabled, boolean reloadOnChange);
2396    private native boolean nativeGetUseDesktopUserAgent(int nativeContentViewCoreImpl);
2397
2398    private native void nativeClearSslPreferences(int nativeContentViewCoreImpl);
2399
2400    private native void nativeAddJavascriptInterface(int nativeContentViewCoreImpl, Object object,
2401                                                     String name, boolean requireAnnotation);
2402
2403    private native void nativeRemoveJavascriptInterface(int nativeContentViewCoreImpl, String name);
2404
2405    private native int nativeGetNavigationHistory(int nativeContentViewCoreImpl, Object context);
2406
2407    private native void nativeUpdateVSyncParameters(int nativeContentViewCoreImpl,
2408            long timebaseMicros, long intervalMicros);
2409
2410    private native boolean nativePopulateBitmapFromCompositor(int nativeContentViewCoreImpl,
2411            Bitmap bitmap);
2412
2413    private native void nativeSetSize(int nativeContentViewCoreImpl, int width, int height);
2414}
2415