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.
5 package org.chromium.content.browser;
7 import android.app.Activity;
8 import android.content.Context;
9 import android.content.res.Configuration;
10 import android.graphics.Bitmap;
11 import android.graphics.Canvas;
12 import android.graphics.Rect;
13 import android.os.Build;
14 import android.util.AttributeSet;
15 import android.view.KeyEvent;
16 import android.view.MotionEvent;
17 import android.view.View;
18 import android.view.accessibility.AccessibilityEvent;
19 import android.view.accessibility.AccessibilityNodeInfo;
20 import android.view.inputmethod.EditorInfo;
21 import android.view.inputmethod.InputConnection;
22 import android.widget.FrameLayout;
24 import com.google.common.annotations.VisibleForTesting;
26 import org.chromium.content.common.TraceEvent;
27 import org.chromium.ui.WindowAndroid;
30 * The containing view for {@link ContentViewCore} that exists in the Android UI hierarchy and
31 * exposes the various {@link View} functionality to it.
33 * TODO(joth): Remove any methods overrides from this class that were added for WebView
36 public class ContentView extends FrameLayout
37 implements ContentViewCore.InternalAccessDelegate, PageInfo {
39 private final ContentViewCore mContentViewCore;
41 private float mCurrentTouchOffsetX;
42 private float mCurrentTouchOffsetY;
43 private final int[] mLocationInWindow = new int[2];
46 * Creates an instance of a ContentView.
47 * @param context The Context the view is running in, through which it can
48 * access the current theme, resources, etc.
49 * @param nativeWebContents A pointer to the native web contents.
50 * @param windowAndroid An instance of the WindowAndroid.
51 * @return A ContentView instance.
53 public static ContentView newInstance(Context context, int nativeWebContents,
54 WindowAndroid windowAndroid) {
55 return newInstance(context, nativeWebContents, windowAndroid, null,
56 android.R.attr.webViewStyle);
60 * Creates an instance of a ContentView.
61 * @param context The Context the view is running in, through which it can
62 * access the current theme, resources, etc.
63 * @param nativeWebContents A pointer to the native web contents.
64 * @param windowAndroid An instance of the WindowAndroid.
65 * @param attrs The attributes of the XML tag that is inflating the view.
66 * @return A ContentView instance.
68 public static ContentView newInstance(Context context, int nativeWebContents,
69 WindowAndroid windowAndroid, AttributeSet attrs) {
70 // TODO(klobag): use the WebViewStyle as the default style for now. It enables scrollbar.
71 // When ContentView is moved to framework, we can define its own style in the res.
72 return newInstance(context, nativeWebContents, windowAndroid, attrs,
73 android.R.attr.webViewStyle);
77 * Creates an instance of a ContentView.
78 * @param context The Context the view is running in, through which it can
79 * access the current theme, resources, etc.
80 * @param nativeWebContents A pointer to the native web contents.
81 * @param windowAndroid An instance of the WindowAndroid.
82 * @param attrs The attributes of the XML tag that is inflating the view.
83 * @param defStyle The default style to apply to this view.
84 * @return A ContentView instance.
86 public static ContentView newInstance(Context context, int nativeWebContents,
87 WindowAndroid windowAndroid, AttributeSet attrs, int defStyle) {
88 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
89 return new ContentView(context, nativeWebContents, windowAndroid, attrs, defStyle);
91 return new JellyBeanContentView(context, nativeWebContents, windowAndroid, attrs,
96 protected ContentView(Context context, int nativeWebContents, WindowAndroid windowAndroid,
97 AttributeSet attrs, int defStyle) {
98 super(context, attrs, defStyle);
100 if (getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
101 setHorizontalScrollBarEnabled(false);
102 setVerticalScrollBarEnabled(false);
106 setFocusableInTouchMode(true);
108 mContentViewCore = new ContentViewCore(context);
109 mContentViewCore.initialize(this, this, nativeWebContents, windowAndroid,
110 Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN ?
111 ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC :
112 ContentViewCore.INPUT_EVENTS_DELIVERED_IMMEDIATELY);
116 * @return The URL of the page.
118 public String getUrl() {
119 return mContentViewCore.getUrl();
122 // PageInfo implementation.
125 public String getTitle() {
126 return mContentViewCore.getTitle();
130 public boolean isReadyForSnapshot() {
131 return !isCrashed() && isReady();
135 public Bitmap getBitmap() {
136 return getBitmap(getWidth(), getHeight());
140 public Bitmap getBitmap(int width, int height) {
141 return mContentViewCore.getBitmap(width, height);
145 public int getBackgroundColor() {
146 return mContentViewCore.getBackgroundColor();
150 public View getView() {
155 * @return The core component of the ContentView that handles JNI communication. Should only be
156 * used for passing to native.
158 public ContentViewCore getContentViewCore() {
159 return mContentViewCore;
163 * @return The cache of scales and positions used to convert coordinates from/to CSS.
165 public RenderCoordinates getRenderCoordinates() {
166 return mContentViewCore.getRenderCoordinates();
170 * Returns true if the given Activity has hardware acceleration enabled
171 * in its manifest, or in its foreground window.
173 * TODO(husky): Remove when ContentViewCore.initialize() is refactored (see TODO there)
174 * TODO(dtrainor) This is still used by other classes. Make sure to pull some version of this
175 * out before removing it.
177 public static boolean hasHardwareAcceleration(Activity activity) {
178 return ContentViewCore.hasHardwareAcceleration(activity);
182 * Destroy the internal state of the WebView. This method may only be called
183 * after the WebView has been removed from the view system. No other methods
184 * may be called on this WebView after this method has been called.
186 public void destroy() {
187 mContentViewCore.destroy();
191 * Returns true initially, false after destroy() has been called.
192 * It is illegal to call any other public method after destroy().
194 public boolean isAlive() {
195 return mContentViewCore.isAlive();
199 * For internal use. Throws IllegalStateException if mNativeContentView is 0.
200 * Use this to ensure we get a useful Java stack trace, rather than a native
201 * crash dump, from use-after-destroy bugs in Java code.
203 void checkIsAlive() throws IllegalStateException {
204 mContentViewCore.checkIsAlive();
207 public void setContentViewClient(ContentViewClient client) {
208 mContentViewCore.setContentViewClient(client);
212 public ContentViewClient getContentViewClient() {
213 return mContentViewCore.getContentViewClient();
217 * Load url without fixing up the url string. Consumers of ContentView are responsible for
218 * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
219 * off during user input).
221 * @param params Parameters for this load.
223 public void loadUrl(LoadUrlParams params) {
224 mContentViewCore.loadUrl(params);
228 * Stops loading the current web contents.
230 public void stopLoading() {
231 mContentViewCore.stopLoading();
235 * @return Whether the current WebContents has a previous navigation entry.
237 public boolean canGoBack() {
238 return mContentViewCore.canGoBack();
242 * @return Whether the current WebContents has a navigation entry after the current one.
244 public boolean canGoForward() {
245 return mContentViewCore.canGoForward();
249 * @param offset The offset into the navigation history.
250 * @return Whether we can move in history by given offset
252 public boolean canGoToOffset(int offset) {
253 return mContentViewCore.canGoToOffset(offset);
257 * Navigates to the specified offset from the "current entry". Does nothing if the offset is out
259 * @param offset The offset into the navigation history.
261 public void goToOffset(int offset) {
262 mContentViewCore.goToOffset(offset);
266 * Goes to the navigation entry before the current one.
268 public void goBack() {
269 mContentViewCore.goBack();
273 * Goes to the navigation entry following the current one.
275 public void goForward() {
276 mContentViewCore.goForward();
280 * Reload the current page.
282 public void reload() {
283 mContentViewCore.reload();
287 * Clears the WebView's page history in both the backwards and forwards
290 public void clearHistory() {
291 mContentViewCore.clearHistory();
295 * Start profiling the update speed. You must call {@link #stopFpsProfiling}
299 public void startFpsProfiling() {
300 // TODO(nileshagrawal): Implement this.
304 * Stop profiling the update speed.
307 public float stopFpsProfiling() {
308 // TODO(nileshagrawal): Implement this.
313 * Fling the ContentView from the current position.
314 * @param x Fling touch starting position
315 * @param y Fling touch starting position
316 * @param velocityX Initial velocity of the fling (X) measured in pixels per second.
317 * @param velocityY Initial velocity of the fling (Y) measured in pixels per second.
320 public void fling(long timeMs, int x, int y, int velocityX, int velocityY) {
321 mContentViewCore.getContentViewGestureHandler().fling(timeMs, x, y, velocityX, velocityY);
325 * Start pinch zoom. You must call {@link #pinchEnd} to stop.
328 public void pinchBegin(long timeMs, int x, int y) {
329 mContentViewCore.getContentViewGestureHandler().pinchBegin(timeMs, x, y);
336 public void pinchEnd(long timeMs) {
337 mContentViewCore.getContentViewGestureHandler().pinchEnd(timeMs);
340 void setIgnoreSingleTap(boolean value) {
341 mContentViewCore.getContentViewGestureHandler().setIgnoreSingleTap(value);
344 /** @see ContentViewGestureHandler#setIgnoreRemainingTouchEvents */
345 public void setIgnoreRemainingTouchEvents() {
346 mContentViewCore.getContentViewGestureHandler().setIgnoreRemainingTouchEvents();
350 * Modify the ContentView magnification level. The effect of calling this
351 * method is exactly as after "pinch zoom".
353 * @param timeMs The event time in milliseconds.
354 * @param delta The ratio of the new magnification level over the current
355 * magnification level.
356 * @param anchorX The magnification anchor (X) in the current view
358 * @param anchorY The magnification anchor (Y) in the current view
362 public void pinchBy(long timeMs, int anchorX, int anchorY, float delta) {
363 mContentViewCore.getContentViewGestureHandler().pinchBy(timeMs, anchorX, anchorY, delta);
367 * Injects the passed JavaScript code in the current page and evaluates it.
369 * @throws IllegalStateException If the ContentView has been destroyed.
371 public void evaluateJavaScript(String script) throws IllegalStateException {
372 mContentViewCore.evaluateJavaScript(script, null);
376 * To be called when the ContentView is shown.
378 public void onShow() {
379 mContentViewCore.onShow();
383 * To be called when the ContentView is hidden.
385 public void onHide() {
386 mContentViewCore.onHide();
390 * Return the ContentSettings object used to retrieve the settings for this
392 * @return A ContentSettings object that can be used to retrieve this ContentView's
395 public ContentSettings getContentSettings() {
396 return mContentViewCore.getContentSettings();
400 * Hides the select action bar.
402 public void hideSelectActionBar() {
403 mContentViewCore.hideSelectActionBar();
406 // FrameLayout overrides.
408 // Needed by ContentViewCore.InternalAccessDelegate
410 public boolean drawChild(Canvas canvas, View child, long drawingTime) {
411 return super.drawChild(canvas, child, drawingTime);
414 // Needed by ContentViewCore.InternalAccessDelegate
416 public void onScrollChanged(int l, int t, int oldl, int oldt) {
417 super.onScrollChanged(l, t, oldl, oldt);
421 protected void onSizeChanged(int w, int h, int ow, int oh) {
423 super.onSizeChanged(w, h, ow, oh);
424 mContentViewCore.onSizeChanged(w, h, ow, oh);
429 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
430 super.onLayout(changed, left, top, right, bottom);
432 getLocationInWindow(mLocationInWindow);
433 mContentViewCore.onLocationInWindowChanged(mLocationInWindow[0], mLocationInWindow[1]);
438 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
439 return mContentViewCore.onCreateInputConnection(outAttrs);
443 public boolean onCheckIsTextEditor() {
444 return mContentViewCore.onCheckIsTextEditor();
448 protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
450 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
451 mContentViewCore.onFocusChanged(gainFocus);
456 public void onWindowFocusChanged(boolean hasWindowFocus) {
457 super.onWindowFocusChanged(hasWindowFocus);
458 mContentViewCore.onWindowFocusChanged(hasWindowFocus);
462 public boolean onKeyUp(int keyCode, KeyEvent event) {
463 return mContentViewCore.onKeyUp(keyCode, event);
467 public boolean dispatchKeyEventPreIme(KeyEvent event) {
468 return mContentViewCore.dispatchKeyEventPreIme(event);
472 public boolean dispatchKeyEvent(KeyEvent event) {
474 return mContentViewCore.dispatchKeyEvent(event);
476 return super.dispatchKeyEvent(event);
481 public boolean onTouchEvent(MotionEvent event) {
482 MotionEvent offset = createOffsetMotionEvent(event);
483 boolean consumed = mContentViewCore.onTouchEvent(offset);
489 * Mouse move events are sent on hover enter, hover move and hover exit.
490 * They are sent on hover exit because sometimes it acts as both a hover
491 * move and hover exit.
494 public boolean onHoverEvent(MotionEvent event) {
495 MotionEvent offset = createOffsetMotionEvent(event);
496 boolean consumed = mContentViewCore.onHoverEvent(offset);
498 super.onHoverEvent(event);
503 public boolean onGenericMotionEvent(MotionEvent event) {
504 return mContentViewCore.onGenericMotionEvent(event);
508 public boolean performLongClick() {
513 * Sets the current amount to offset incoming touch events by. This is used to handle content
514 * moving and not lining up properly with the android input system.
515 * @param dx The X offset in pixels to shift touch events.
516 * @param dy The Y offset in pixels to shift touch events.
518 public void setCurrentMotionEventOffsets(float dx, float dy) {
519 mCurrentTouchOffsetX = dx;
520 mCurrentTouchOffsetY = dy;
523 private MotionEvent createOffsetMotionEvent(MotionEvent src) {
524 MotionEvent dst = MotionEvent.obtain(src);
525 dst.offsetLocation(mCurrentTouchOffsetX, mCurrentTouchOffsetY);
530 protected void onConfigurationChanged(Configuration newConfig) {
531 mContentViewCore.onConfigurationChanged(newConfig);
535 * Currently the ContentView scrolling happens in the native side. In
536 * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
537 * are overridden, so that View's mScrollX and mScrollY will be unchanged at
538 * (0, 0). This is critical for drawing ContentView correctly.
541 public void scrollBy(int x, int y) {
542 mContentViewCore.scrollBy(x, y);
546 public void scrollTo(int x, int y) {
547 mContentViewCore.scrollTo(x, y);
551 protected int computeHorizontalScrollExtent() {
552 // TODO (dtrainor): Need to expose scroll events properly to public. Either make getScroll*
553 // work or expose computeHorizontalScrollOffset()/computeVerticalScrollOffset as public.
554 return mContentViewCore.computeHorizontalScrollExtent();
558 protected int computeHorizontalScrollOffset() {
559 return mContentViewCore.computeHorizontalScrollOffset();
563 protected int computeHorizontalScrollRange() {
564 return mContentViewCore.computeHorizontalScrollRange();
568 protected int computeVerticalScrollExtent() {
569 return mContentViewCore.computeVerticalScrollExtent();
573 protected int computeVerticalScrollOffset() {
574 return mContentViewCore.computeVerticalScrollOffset();
578 protected int computeVerticalScrollRange() {
579 return mContentViewCore.computeVerticalScrollRange();
582 // End FrameLayout overrides.
585 public boolean awakenScrollBars(int startDelay, boolean invalidate) {
586 return mContentViewCore.awakenScrollBars(startDelay, invalidate);
590 public boolean awakenScrollBars() {
591 return super.awakenScrollBars();
594 public int getSingleTapX() {
595 return mContentViewCore.getContentViewGestureHandler().getSingleTapX();
598 public int getSingleTapY() {
599 return mContentViewCore.getContentViewGestureHandler().getSingleTapY();
603 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
604 super.onInitializeAccessibilityNodeInfo(info);
605 mContentViewCore.onInitializeAccessibilityNodeInfo(info);
609 * Fills in scrolling values for AccessibilityEvents.
610 * @param event Event being fired.
613 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
614 super.onInitializeAccessibilityEvent(event);
615 mContentViewCore.onInitializeAccessibilityEvent(event);
619 protected void onAttachedToWindow() {
620 super.onAttachedToWindow();
621 mContentViewCore.onAttachedToWindow();
625 protected void onDetachedFromWindow() {
626 super.onDetachedFromWindow();
627 mContentViewCore.onDetachedFromWindow();
631 protected void onVisibilityChanged(View changedView, int visibility) {
632 super.onVisibilityChanged(changedView, visibility);
633 mContentViewCore.onVisibilityChanged(changedView, visibility);
637 * Register the delegate to be used when content can not be handled by
638 * the rendering engine, and should be downloaded instead. This will replace
639 * the current delegate.
640 * @param delegate An implementation of ContentViewDownloadDelegate.
642 public void setDownloadDelegate(ContentViewDownloadDelegate delegate) {
643 mContentViewCore.setDownloadDelegate(delegate);
646 // Called by DownloadController.
647 ContentViewDownloadDelegate getDownloadDelegate() {
648 return mContentViewCore.getDownloadDelegate();
651 public boolean getUseDesktopUserAgent() {
652 return mContentViewCore.getUseDesktopUserAgent();
656 * Set whether or not we're using a desktop user agent for the currently loaded page.
657 * @param override If true, use a desktop user agent. Use a mobile one otherwise.
658 * @param reloadOnChange Reload the page if the UA has changed.
660 public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange) {
661 mContentViewCore.setUseDesktopUserAgent(override, reloadOnChange);
665 * @return Whether the native ContentView has crashed.
667 public boolean isCrashed() {
668 return mContentViewCore.isCrashed();
672 * Zooms in the WebView by 25% (or less if that would result in zooming in
673 * more than possible).
675 * @return True if there was a zoom change, false otherwise.
677 // This method uses the term 'zoom' for legacy reasons, but relates
678 // to what chrome calls the 'page scale factor'.
679 public boolean zoomIn() {
680 return mContentViewCore.zoomIn();
684 * Zooms out the WebView by 20% (or less if that would result in zooming out
685 * more than possible).
687 * @return True if there was a zoom change, false otherwise.
689 // This method uses the term 'zoom' for legacy reasons, but relates
690 // to what chrome calls the 'page scale factor'.
691 public boolean zoomOut() {
692 return mContentViewCore.zoomOut();
696 * Resets the zoom factor of the WebView.
698 * @return True if there was a zoom change, false otherwise.
700 // This method uses the term 'zoom' for legacy reasons, but relates
701 // to what chrome calls the 'page scale factor'.
702 public boolean zoomReset() {
703 return mContentViewCore.zoomReset();
707 * Return the current scale of the WebView
708 * @return The current scale.
710 public float getScale() {
711 return mContentViewCore.getScale();
715 * If the view is ready to draw contents to the screen. In hardware mode,
716 * the initialization of the surface texture may not occur until after the
717 * view has been added to the layout. This method will return {@code true}
718 * once the texture is actually ready.
720 public boolean isReady() {
721 return mContentViewCore.isReady();
725 * Returns whether or not accessibility injection is being used.
727 public boolean isInjectingAccessibilityScript() {
728 return mContentViewCore.isInjectingAccessibilityScript();
732 * Enable or disable accessibility features.
734 public void setAccessibilityState(boolean state) {
735 mContentViewCore.setAccessibilityState(state);
739 * Stop any TTS notifications that are currently going on.
741 public void stopCurrentAccessibilityNotifications() {
742 mContentViewCore.stopCurrentAccessibilityNotifications();
746 * Inform WebKit that Fullscreen mode has been exited by the user.
748 public void exitFullscreen() {
749 mContentViewCore.exitFullscreen();
753 * Return content scroll y.
755 * @return The vertical scroll position in pixels.
757 public int getContentScrollY() {
758 return mContentViewCore.computeVerticalScrollOffset();
762 * Return content height.
764 * @return The height of the content in pixels.
766 public int getContentHeight() {
767 return mContentViewCore.computeVerticalScrollRange();
770 ///////////////////////////////////////////////////////////////////////////////////////////////
771 // Start Implementation of ContentViewCore.InternalAccessDelegate //
772 ///////////////////////////////////////////////////////////////////////////////////////////////
775 public boolean super_onKeyUp(int keyCode, KeyEvent event) {
776 return super.onKeyUp(keyCode, event);
780 public boolean super_dispatchKeyEventPreIme(KeyEvent event) {
781 return super.dispatchKeyEventPreIme(event);
785 public boolean super_dispatchKeyEvent(KeyEvent event) {
786 return super.dispatchKeyEvent(event);
790 public boolean super_onGenericMotionEvent(MotionEvent event) {
791 return super.onGenericMotionEvent(event);
795 public void super_onConfigurationChanged(Configuration newConfig) {
796 super.onConfigurationChanged(newConfig);
800 public boolean super_awakenScrollBars(int startDelay, boolean invalidate) {
801 return super.awakenScrollBars(startDelay, invalidate);
804 ///////////////////////////////////////////////////////////////////////////////////////////////
805 // End Implementation of ContentViewCore.InternalAccessDelegate //
806 ///////////////////////////////////////////////////////////////////////////////////////////////