FileDocCategorySizeDatePackage
WebView.javaAPI DocAndroid 1.5 API206632Wed May 06 22:41:56 BST 2009android.webkit

WebView

public class WebView extends android.widget.AbsoluteLayout implements ViewTreeObserver.OnGlobalFocusChangeListener, ViewGroup.OnHierarchyChangeListener

A View that displays web pages. This class is the basis upon which you can roll your own web browser or simply display some online content within your Activity. It uses the WebKit rendering engine to display web pages and includes methods to navigate forward and backward through a history, zoom in and out, perform text searches and more.

To enable the built-in zoom, set {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)} (introduced in API version 3).

Note that, in order for your Activity to access the Internet and load web pages in a WebView, you must add the INTERNET permissions to your Android Manifest file:

<uses-permission android:name="android.permission.INTERNET" />

This must be a child of the <manifest> element.

Fields Summary
private static final boolean
AUTO_REDRAW_HACK
private boolean
mAutoRedraw
static final String
LOGTAG
static final boolean
DEBUG
static final boolean
LOGV_ENABLED
private final CallbackProxy
mCallbackProxy
private final WebViewDatabase
mDatabase
private android.net.http.SslCertificate
mCertificate
private int
mNativeClass
private WebViewCore
mWebViewCore
final android.os.Handler
mPrivateHandler
private TextDialog
mTextEntry
private int
mTextGeneration
private static PluginList
sPluginList
private float
mLastTouchX
Position of the last touch event.
private float
mLastTouchY
private long
mLastTouchTime
Time of the last touch event.
private long
mLastSentTouchTime
Time of the last time sending touch event to WebViewCore
private static final int
TOUCH_SENT_INTERVAL
The minimum elapsed time before sending another ACTION_MOVE event to WebViewCore
android.view.VelocityTracker
mVelocityTracker
Helper class to get velocity for fling
private int
mTouchMode
Touch mode
private static final int
TOUCH_INIT_MODE
private static final int
TOUCH_DRAG_START_MODE
private static final int
TOUCH_DRAG_MODE
private static final int
TOUCH_SHORTPRESS_START_MODE
private static final int
TOUCH_SHORTPRESS_MODE
private static final int
TOUCH_DOUBLECLICK_MODE
private static final int
TOUCH_DONE_MODE
private static final int
TOUCH_SELECT_MODE
private static final int
FIRST_SCROLL_ZOOM
private static final int
SCROLL_ZOOM_ANIMATION_IN
private static final int
SCROLL_ZOOM_ANIMATION_OUT
private static final int
SCROLL_ZOOM_OUT
private static final int
LAST_SCROLL_ZOOM
private boolean
mForwardTouchEvents
private boolean
mPreventDrag
private boolean
mNeedsUpdateTextEntry
private boolean
mDrawFocusRing
private int
mTouchSlopSquare
Customizable constant
private int
mNavSlop
private static final int
TAP_TIMEOUT
private static final int
LONG_PRESS_TIMEOUT
private static final int
MIN_FLING_TIME
private static final long
ZOOM_CONTROLS_TIMEOUT
private static final int
PAGE_SCROLL_OVERLAP
boolean
mWidthCanMeasure
These prevent calling requestLayout if either dimension is fixed. This depends on the layout parameters and the measure specs.
boolean
mHeightCanMeasure
int
mLastWidthSent
int
mLastHeightSent
private int
mContentWidth
private int
mContentHeight
private boolean
mOverlayHorizontalScrollbar
private boolean
mOverlayVerticalScrollbar
private static final int
STD_SPEED
private static final int
MAX_DURATION
private android.widget.Scroller
mScroller
private boolean
mWrapContent
private WebViewCore.FocusData
mFocusData
private static final int
REMEMBER_PASSWORD
Private message ids
private static final int
NEVER_REMEMBER_PASSWORD
private static final int
SWITCH_TO_SHORTPRESS
private static final int
SWITCH_TO_LONGPRESS
private static final int
UPDATE_TEXT_ENTRY_ADAPTER
private static final int
SWITCH_TO_ENTER
private static final int
RESUME_WEBCORE_UPDATE
static final int
SCROLL_TO_MSG_ID
static final int
SCROLL_BY_MSG_ID
static final int
SPAWN_SCROLL_TO_MSG_ID
static final int
SYNC_SCROLL_TO_MSG_ID
static final int
NEW_PICTURE_MSG_ID
static final int
UPDATE_TEXT_ENTRY_MSG_ID
static final int
WEBCORE_INITIALIZED_MSG_ID
static final int
UPDATE_TEXTFIELD_TEXT_MSG_ID
static final int
DID_FIRST_LAYOUT_MSG_ID
static final int
RECOMPUTE_FOCUS_MSG_ID
static final int
NOTIFY_FOCUS_SET_MSG_ID
static final int
MARK_NODE_INVALID_ID
static final int
UPDATE_CLIPBOARD
static final int
LONG_PRESS_ENTER
static final int
PREVENT_TOUCH_ID
static final int
WEBCORE_NEED_TOUCH_EVENTS
static final int
INVAL_RECT_MSG_ID
static final String[]
HandlerDebugString
static final int
ZOOM_OUT_WIDTH
private static final float
DEFAULT_MAX_ZOOM_SCALE
private static final float
DEFAULT_MIN_ZOOM_SCALE
private float
mMaxZoomScale
private float
mMinZoomScale
private boolean
mMinZoomScaleFixed
private int
mInitialScale
private boolean
mPreviewZoomOnly
private float
mActualScale
private float
mInvActualScale
private float
mZoomScale
private float
mInvInitialZoomScale
private float
mInvFinalZoomScale
private long
mZoomStart
private static final int
ZOOM_ANIMATION_LENGTH
private boolean
mUserScroll
private int
mSnapScrollMode
private static final int
SNAP_NONE
private static final int
SNAP_X
private static final int
SNAP_Y
private static final int
SNAP_X_LOCK
private static final int
SNAP_Y_LOCK
private boolean
mSnapPositive
private boolean
mGotKeyDown
static boolean
mLogEvent
private static final int
EVENT_LOG_ZOOM_LEVEL_CHANGE
private static final int
EVENT_LOG_DOUBLE_TAP_DURATION
private long
mLastTouchUpTime
public static final String
SCHEME_TEL
URI scheme for telephone number
public static final String
SCHEME_MAILTO
URI scheme for email address
public static final String
SCHEME_GEO
URI scheme for map address
private int
mBackgroundColor
private PictureListener
mPictureListener
private ExtendedZoomControls
mZoomControls
private Runnable
mZoomControlRunnable
private android.widget.ZoomButtonsController
mZoomButtonsController
private android.widget.ImageView
mZoomOverviewButton
private android.widget.ImageView
mZoomFitPageButton
private float
mZoomCenterX
private float
mZoomCenterY
private ZoomButtonsController.OnZoomListener
mZoomListener
private android.graphics.Rect
mLastVisibleRectSent
private android.graphics.Rect
mLastGlobalRect
private static final int
FIND_HEIGHT
private boolean
mFindIsUp
private float
mZoomScrollLimit
private float
mZoomScrollInvLimit
private int
mLastScrollX
private int
mLastScrollY
private long
mZoomScrollStart
private int
mZoomScrollX
private int
mZoomScrollY
private float
mLastZoomScrollRawX
private float
mLastZoomScrollRawY
private static final int
SCROLL_ZOOM_GRID
private static final int
SCROLL_ZOOM_DURATION
private static final int
SCROLL_ZOOM_FINGER_BUFFER
private boolean
mDrawHistory
private android.graphics.Picture
mHistoryPicture
private int
mHistoryWidth
private int
mHistoryHeight
private FocusNode
mFocusNode
private boolean
mGotEnterDown
private int
mMinLockSnapReverseDistance
private static final float
MAX_SLOPE_FOR_DIAG
private static final int
MIN_BREAK_SNAP_CROSS_DISTANCE
private long
mTrackballFirstTime
private long
mTrackballLastTime
private float
mTrackballRemainsX
private float
mTrackballRemainsY
private int
mTrackballXMove
private int
mTrackballYMove
private boolean
mExtendSelection
private boolean
mTouchSelection
private static final int
TRACKBALL_KEY_TIMEOUT
private static final int
TRACKBALL_TIMEOUT
private static final int
TRACKBALL_WAIT
private static final int
TRACKBALL_SCALE
private static final int
TRACKBALL_SCROLL_COUNT
private static final int
TRACKBALL_MOVE_COUNT
private static final int
TRACKBALL_MULTIPLIER
private static final int
SELECT_CURSOR_OFFSET
private int
mSelectX
private int
mSelectY
private boolean
mShiftIsPressed
private boolean
mTrackballDown
private long
mTrackballUpTime
private long
mLastFocusTime
private android.graphics.Rect
mLastFocusBounds
private boolean
mMapTrackballToArrowKeys
Constructors Summary
public WebView(android.content.Context context)
Construct a new WebView with a Context object.

param
context A Context object used to access application assets.

    
                           
       
        this(context, null);
    
public WebView(android.content.Context context, android.util.AttributeSet attrs)
Construct a new WebView with layout parameters.

param
context A Context object used to access application assets.
param
attrs An AttributeSet passed to our parent.

        this(context, attrs, com.android.internal.R.attr.webViewStyle);
    
public WebView(android.content.Context context, android.util.AttributeSet attrs, int defStyle)
Construct a new WebView with layout parameters and a default style.

param
context A Context object used to access application assets.
param
attrs An AttributeSet passed to our parent.
param
defStyle The default style resource ID.

        super(context, attrs, defStyle);
        init();

        mCallbackProxy = new CallbackProxy(context, this);
        mWebViewCore = new WebViewCore(context, this, mCallbackProxy);
        mDatabase = WebViewDatabase.getInstance(context);
        mFocusData = new WebViewCore.FocusData();
        mFocusData.mFrame = 0;
        mFocusData.mNode = 0;
        mFocusData.mX = 0;
        mFocusData.mY = 0;
        mScroller = new Scroller(context);

        initZoomController(context);
    
Methods Summary
public voidaddJavascriptInterface(java.lang.Object obj, java.lang.String interfaceName)
Use this function to bind an object to Javascript so that the methods can be accessed from Javascript.

IMPORTANT:

  • Using addJavascriptInterface() allows JavaScript to control your application. This can be a very useful feature or a dangerous security issue. When the HTML in the WebView is untrustworthy (for example, part or all of the HTML is provided by some person or process), then an attacker could inject HTML that will execute your code and possibly any code of the attacker's choosing.
    Do not use addJavascriptInterface() unless all of the HTML in this WebView was written by you.
  • The Java object that is bound runs in another thread and not in the thread that it was constructed in.

param
obj The class instance to bind to Javascript
param
interfaceName The name to used to expose the class in Javascript

        // Use Hashmap rather than Bundle as Bundles can't cope with Objects
        HashMap arg = new HashMap();
        arg.put("object", obj);
        arg.put("interfaceName", interfaceName);
        mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
    
private voidcalcOurContentVisibleRect(android.graphics.Rect r)

        calcOurVisibleRect(r);
        r.left = viewToContent(r.left);
        r.top = viewToContent(r.top);
        r.right = viewToContent(r.right);
        r.bottom = viewToContent(r.bottom);
    
private voidcalcOurVisibleRect(android.graphics.Rect r)

        Point p = new Point();
        getGlobalVisibleRect(r, p);
        r.offset(-p.x, -p.y);
    
public booleancanGoBack()
Return true if this WebView has a back history item.

return
True iff this WebView has a back history item.

        WebBackForwardList l = mCallbackProxy.getBackForwardList();
        synchronized (l) {
            if (l.getClearPending()) {
                return false;
            } else {
                return l.getCurrentIndex() > 0;
            }
        }
    
public booleancanGoBackOrForward(int steps)
Return true if the page can go back or forward the given number of steps.

param
steps The negative or positive number of steps to move the history.

        WebBackForwardList l = mCallbackProxy.getBackForwardList();
        synchronized (l) {
            if (l.getClearPending()) {
                return false;
            } else {
                int newIndex = l.getCurrentIndex() + steps;
                return newIndex >= 0 && newIndex < l.getSize();
            }
        }
    
public booleancanGoForward()
Return true if this WebView has a forward history item.

return
True iff this Webview has a forward history item.

        WebBackForwardList l = mCallbackProxy.getBackForwardList();
        synchronized (l) {
            if (l.getClearPending()) {
                return false;
            } else {
                return l.getCurrentIndex() < l.getSize() - 1;
            }
        }
    
private booleancanZoomScrollOut()

        if (mContentWidth == 0 || mContentHeight == 0) {
            return false;
        }
        int width = getViewWidth();
        int height = getViewHeight();
        float x = (float) width / (float) mContentWidth;
        float y = (float) height / (float) mContentHeight;
        mZoomScrollLimit = Math.max(DEFAULT_MIN_ZOOM_SCALE, Math.min(x, y));
        mZoomScrollInvLimit = 1.0f / mZoomScrollLimit;
        if (LOGV_ENABLED) {
            Log.v(LOGTAG, "canZoomScrollOut"
                    + " mInvActualScale=" + mInvActualScale
                    + " mZoomScrollLimit=" + mZoomScrollLimit
                    + " mZoomScrollInvLimit=" + mZoomScrollInvLimit
                    + " mContentWidth=" + mContentWidth
                    + " mContentHeight=" + mContentHeight
                    );
        }
        // don't zoom out unless magnify area is at least half as wide
        // or tall as content
        float limit = mZoomScrollLimit * 2;
        return mContentWidth >= width * limit
                || mContentHeight >= height * limit;
    
public android.graphics.PicturecapturePicture()
Return a new picture that captures the current display of the webview. This is a copy of the display, and will be unaffected if the webview later loads a different URL.

return
a picture containing the current contents of the view. Note this picture is of the entire document, and is not restricted to the bounds of the view.

        if (null == mWebViewCore) return null; // check for out of memory tab 
        return mWebViewCore.copyContentPicture();
    
public voidclearCache(boolean includeDiskFiles)
Clear the resource cache. This will cause resources to be re-downloaded if accessed again.

Note: this really needs to be a static method as it clears cache for all WebView. But we need mWebViewCore to send message to WebCore thread, so we can't make this static.

        mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
                includeDiskFiles ? 1 : 0, 0);
    
public voidclearFormData()
Make sure that clearing the form data removes the adapter from the currently focused textfield if there is one.

        if (inEditingMode()) {
            AutoCompleteAdapter adapter = null;
            mTextEntry.setAdapterCustom(adapter);
        }
    
public voidclearHistory()
Tell the WebView to clear its internal back/forward list.

        mCallbackProxy.getBackForwardList().setClearPending();
        mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
    
public voidclearMatches()

        mFindIsUp = false;
        nativeSetFindIsDown();
        // Now that the dialog has been removed, ensure that we scroll to a
        // location that is not beyond the end of the page.
        pinScrollTo(mScrollX, mScrollY, false, 0);
        invalidate();
    
public voidclearSslPreferences()
Clear the SSL preferences table stored in response to proceeding with SSL certificate errors.

        mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
    
private voidclearTextEntry()

        if (inEditingMode()) {
            mTextEntry.remove();
        }
    
public voidclearView()
Clear the view so that onDraw() will draw nothing but white background, and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY

        mContentWidth = 0;
        mContentHeight = 0;
        mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
    
private booleancommitCopy()

        boolean copiedSomething = false;
        if (mExtendSelection) {
            // copy region so core operates on copy without touching orig.
            Region selection = new Region(nativeGetSelection());
            if (selection.isEmpty() == false) {
                Toast.makeText(mContext
                        , com.android.internal.R.string.text_copied
                        , Toast.LENGTH_SHORT).show();
                mWebViewCore.sendMessage(EventHub.GET_SELECTION, selection);
                copiedSomething = true;
            }
            mExtendSelection = false;
        }
        mShiftIsPressed = false;
        if (mTouchMode == TOUCH_SELECT_MODE) {
            mTouchMode = TOUCH_INIT_MODE;
        }
        return copiedSomething;
    
private static intcomputeDuration(int dx, int dy)

        int distance = Math.max(Math.abs(dx), Math.abs(dy));
        int duration = distance * 1000 / STD_SPEED;
        return Math.min(duration, MAX_DURATION);
    
protected intcomputeHorizontalScrollRange()

        if (mDrawHistory) {
            return mHistoryWidth;
        } else {
            return contentToView(mContentWidth);
        }
    
public voidcomputeScroll()

        if (mScroller.computeScrollOffset()) {
            int oldX = mScrollX;
            int oldY = mScrollY;
            mScrollX = mScroller.getCurrX();
            mScrollY = mScroller.getCurrY();
            postInvalidate();  // So we draw again
            if (oldX != mScrollX || oldY != mScrollY) {
                // as onScrollChanged() is not called, sendOurVisibleRect()
                // needs to be call explicitly
                sendOurVisibleRect();
            }
        } else {
            super.computeScroll();
        }
    
protected intcomputeVerticalScrollRange()


    
       
        if (mDrawHistory) {
            return mHistoryHeight;
        } else {
            int height = contentToView(mContentHeight);
            if (mFindIsUp) {
                height += FIND_HEIGHT;
            }
            return height;
        }
    
private voidcontentSizeChanged(boolean updateLayout)
These are from webkit, and are in content coordinate system (unzoomed)

        // suppress 0,0 since we usually see real dimensions soon after
        // this avoids drawing the prev content in a funny place. If we find a
        // way to consolidate these notifications, this check may become
        // obsolete
        if ((mContentWidth | mContentHeight) == 0) {
            return;
        }

        if (mHeightCanMeasure) {
            if (getMeasuredHeight() != contentToView(mContentHeight)
                    && updateLayout) {
                requestLayout();
            }
        } else if (mWidthCanMeasure) {
            if (getMeasuredWidth() != contentToView(mContentWidth)
                    && updateLayout) {
                requestLayout();
            }
        } else {
            // If we don't request a layout, try to send our view size to the
            // native side to ensure that WebCore has the correct dimensions.
            sendViewSizeZoom();
        }
    
private intcontentToView(int x)

        return Math.round(x * mActualScale);
    
private android.graphics.RectcontentToView(android.graphics.Rect x)

        return new Rect(contentToView(x.left), contentToView(x.top)
                , contentToView(x.right), contentToView(x.bottom));
    
public WebBackForwardListcopyBackForwardList()
Return the WebBackForwardList for this WebView. This contains the back/forward list for use in querying each item in the history stack. This is a copy of the private WebBackForwardList so it contains only a snapshot of the current state. Multiple calls to this method may return different objects. The object returned from this method will not be updated to reflect any new state.

        return mCallbackProxy.getBackForwardList().clone();
    
private android.webkit.WebView$ExtendedZoomControlscreateZoomControls()

        ExtendedZoomControls zoomControls = new ExtendedZoomControls(mContext
            , null);
        zoomControls.setOnZoomInClickListener(new OnClickListener() {
            public void onClick(View v) {
                // reset time out
                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
                mPrivateHandler.postDelayed(mZoomControlRunnable,
                        ZOOM_CONTROLS_TIMEOUT);
                zoomIn();
            }
        });
        zoomControls.setOnZoomOutClickListener(new OnClickListener() {
            public void onClick(View v) {
                // reset time out
                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
                mPrivateHandler.postDelayed(mZoomControlRunnable,
                        ZOOM_CONTROLS_TIMEOUT);
                zoomOut();
            }
        });
        zoomControls.setOnZoomMagnifyClickListener(new OnClickListener() {
            public void onClick(View v) {
                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
                mPrivateHandler.postDelayed(mZoomControlRunnable,
                        ZOOM_CONTROLS_TIMEOUT);
                zoomScrollOut();
            }
        });
        return zoomControls;
    
public voiddebugDump()

        nativeDebugDump();
        mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
    
voiddeleteSelection(int start, int end)
Delete text from start to end in the focused textfield. If there is no focus, or if start == end, silently fail. If start and end are out of order, swap them.

param
start Beginning of selection to delete.
param
end End of selection to delete.

    
                                                                      
    /* package */      
        mTextGeneration++;
        mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, start, end,
                new WebViewCore.FocusData(mFocusData));
    
public voiddestroy()
Destroy the internal state of the WebView. This method should be called after the WebView has been removed from the view system. No other methods may be called on a WebView after destroy.

        clearTextEntry();
        if (mWebViewCore != null) {
            // Set the handlers to null before destroying WebViewCore so no
            // more messages will be posted. 
            mCallbackProxy.setWebViewClient(null);
            mCallbackProxy.setWebChromeClient(null);
            // Tell WebViewCore to destroy itself
            WebViewCore webViewCore = mWebViewCore;
            mWebViewCore = null; // prevent using partial webViewCore
            webViewCore.destroy();
            // Remove any pending messages that might not be serviced yet.
            mPrivateHandler.removeCallbacksAndMessages(null);
            mCallbackProxy.removeCallbacksAndMessages(null);
            // Wake up the WebCore thread just in case it is waiting for a
            // javascript dialog.
            synchronized (mCallbackProxy) {
                mCallbackProxy.notify();
            }
        }
        if (mNativeClass != 0) {
            nativeDestroy();
            mNativeClass = 0;
        }
    
public static voiddisablePlatformNotifications()
If platform notifications are enabled, this should be called from onPause() or onStop().

        Network.disablePlatformNotifications();
    
public booleandispatchKeyEvent(android.view.KeyEvent event)

        boolean dispatch = true;

        if (!inEditingMode()) {
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                mGotKeyDown = true;
            } else {
                if (!mGotKeyDown) {
                    /*
                     * We got a key up for which we were not the recipient of
                     * the original key down. Don't give it to the view.
                     */
                    dispatch = false;
                }
                mGotKeyDown = false;
            }
        }

        if (dispatch) {
            return super.dispatchKeyEvent(event);
        } else {
            // We didn't dispatch, so let something else handle the key
            return false;
        }
    
private voiddisplaySoftKeyboard()

        InputMethodManager imm = (InputMethodManager)
                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.showSoftInput(mTextEntry, 0);
        mTextEntry.enableScrollOnScreen(true);
        // Now we need to fake a touch event to place the cursor where the
        // user touched.
        AbsoluteLayout.LayoutParams lp = (AbsoluteLayout.LayoutParams)
                mTextEntry.getLayoutParams();
        if (lp != null) {
            // Take the last touch and adjust for the location of the
            // TextDialog.
            float x = mLastTouchX + (float) (mScrollX - lp.x);
            float y = mLastTouchY + (float) (mScrollY - lp.y);
            mTextEntry.fakeTouchEvent(x, y);
        }
    
private voiddoFling()

        if (mVelocityTracker == null) {
            return;
        }
        int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
        int maxY = Math.max(computeVerticalScrollRange() - getViewHeight(), 0);

        mVelocityTracker.computeCurrentVelocity(1000);
        int vx = (int) mVelocityTracker.getXVelocity();
        int vy = (int) mVelocityTracker.getYVelocity();

        if (mSnapScrollMode != SNAP_NONE) {
            if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_X_LOCK) {
                vy = 0;
            } else {
                vx = 0;
            }
        }
        
        if (true /* EMG release: make our fling more like Maps' */) {
            // maps cuts their velocity in half
            vx = vx * 3 / 4;
            vy = vy * 3 / 4;
        }

        mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY);
        // TODO: duration is calculated based on velocity, if the range is
        // small, the animation will stop before duration is up. We may
        // want to calculate how long the animation is going to run to precisely
        // resume the webcore update.
        final int time = mScroller.getDuration();
        mPrivateHandler.sendEmptyMessageDelayed(RESUME_WEBCORE_UPDATE, time);
        invalidate();
    
private voiddoShortPress()

        if (mNativeClass == 0) {
            return;
        }
        switchOutDrawHistory();
        // mLastTouchX and mLastTouchY are the point in the current viewport
        int contentX = viewToContent((int) mLastTouchX + mScrollX);
        int contentY = viewToContent((int) mLastTouchY + mScrollY);
        if (nativeMotionUp(contentX, contentY, mNavSlop, true)) {
            if (mLogEvent) {
                Checkin.updateStats(mContext.getContentResolver(),
                        Checkin.Stats.Tag.BROWSER_SNAP_CENTER, 1, 0.0);
            }
        }
        if (nativeUpdateFocusNode() && !mFocusNode.mIsTextField
                && !mFocusNode.mIsTextArea) {
            playSoundEffect(SoundEffectConstants.CLICK);
        }
    
private voiddoTrackball(long time)

        int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
        if (elapsed == 0) {
            elapsed = TRACKBALL_TIMEOUT;
        }
        float xRate = mTrackballRemainsX * 1000 / elapsed; 
        float yRate = mTrackballRemainsY * 1000 / elapsed;
        if (mShiftIsPressed) {
            moveSelection(xRate, yRate);
            mTrackballRemainsX = mTrackballRemainsY = 0;
            return;
        }
        float ax = Math.abs(xRate);
        float ay = Math.abs(yRate);
        float maxA = Math.max(ax, ay);
        if (LOGV_ENABLED) {
            Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
                    + " xRate=" + xRate
                    + " yRate=" + yRate
                    + " mTrackballRemainsX=" + mTrackballRemainsX
                    + " mTrackballRemainsY=" + mTrackballRemainsY);
        }
        int width = mContentWidth - getViewWidth();
        int height = mContentHeight - getViewHeight();
        if (width < 0) width = 0;
        if (height < 0) height = 0;
        if (mTouchMode == SCROLL_ZOOM_OUT) {
            int oldX = mZoomScrollX;
            int oldY = mZoomScrollY;
            int maxWH = Math.max(width, height);
            mZoomScrollX += scaleTrackballX(xRate, maxWH);
            mZoomScrollY += scaleTrackballY(yRate, maxWH);
            if (LOGV_ENABLED) {
                Log.v(LOGTAG, "doTrackball SCROLL_ZOOM_OUT"
                        + " mZoomScrollX=" + mZoomScrollX 
                        + " mZoomScrollY=" + mZoomScrollY);
            }
            mZoomScrollX = Math.min(width, Math.max(0, mZoomScrollX));
            mZoomScrollY = Math.min(height, Math.max(0, mZoomScrollY));
            if (oldX != mZoomScrollX || oldY != mZoomScrollY) {
                invalidate();
            }
            mTrackballRemainsX = mTrackballRemainsY = 0;
            return;
        }
        ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
        ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
        maxA = Math.max(ax, ay);
        int count = Math.max(0, (int) maxA);
        int oldScrollX = mScrollX;
        int oldScrollY = mScrollY;
        if (count > 0) {
            int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ? 
                    KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN : 
                    mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
                    KeyEvent.KEYCODE_DPAD_RIGHT;
            count = Math.min(count, TRACKBALL_MOVE_COUNT);
            if (LOGV_ENABLED) {
                Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode 
                        + " count=" + count
                        + " mTrackballRemainsX=" + mTrackballRemainsX
                        + " mTrackballRemainsY=" + mTrackballRemainsY);
            }
            if (navHandledKey(selectKeyCode, count, false, time)) {
                playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
            }
            mTrackballRemainsX = mTrackballRemainsY = 0;
        }
        if (count >= TRACKBALL_SCROLL_COUNT) {
            int xMove = scaleTrackballX(xRate, width);
            int yMove = scaleTrackballY(yRate, height);
            if (LOGV_ENABLED) {
                Log.v(LOGTAG, "doTrackball pinScrollBy"
                        + " count=" + count
                        + " xMove=" + xMove + " yMove=" + yMove
                        + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX) 
                        + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY) 
                        );
            }
            if (Math.abs(mScrollX - oldScrollX) > Math.abs(xMove)) {
                xMove = 0;
            }
            if (Math.abs(mScrollY - oldScrollY) > Math.abs(yMove)) {
                yMove = 0;
            }
            if (xMove != 0 || yMove != 0) {
                pinScrollBy(xMove, yMove, true, 0);
            }
            mUserScroll = true;
        } 
        mWebViewCore.sendMessage(EventHub.UNBLOCK_FOCUS);        
    
public voiddocumentAsText(android.os.Message callback)
{@hide}

        mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
    
public voiddocumentHasImages(android.os.Message response)
Query the document to see if it contains any image references. The message object will be dispatched with arg1 being set to 1 if images were found and 0 if the document does not reference any images.

param
response The message that will be dispatched with the result.

        if (response == null) {
            return;
        }
        mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
    
private voiddrawCoreAndFocusRing(android.graphics.Canvas canvas, int color, boolean drawFocus)

        if (mDrawHistory) {
            canvas.scale(mActualScale, mActualScale);
            canvas.drawPicture(mHistoryPicture);
            return;
        }

        boolean animateZoom = mZoomScale != 0;
        boolean animateScroll = !mScroller.isFinished() 
                || mVelocityTracker != null;
        if (animateZoom) {
            float zoomScale;
            int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
            if (interval < ZOOM_ANIMATION_LENGTH) {
                float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
                zoomScale = 1.0f / (mInvInitialZoomScale 
                        + (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
                invalidate();
            } else {
                zoomScale = mZoomScale;
                // set mZoomScale to be 0 as we have done animation
                mZoomScale = 0;
            }
            float scale = (mActualScale - zoomScale) * mInvActualScale;
            float tx = scale * (mZoomCenterX + mScrollX);
            float ty = scale * (mZoomCenterY + mScrollY);

            // this block pins the translate to "legal" bounds. This makes the
            // animation a bit non-obvious, but it means we won't pop when the
            // "real" zoom takes effect
            if (true) {
               // canvas.translate(mScrollX, mScrollY);
                tx -= mScrollX;
                ty -= mScrollY;
                tx = -pinLoc(-Math.round(tx), getViewWidth(), Math
                        .round(mContentWidth * zoomScale));
                ty = -pinLoc(-Math.round(ty), getViewHeight(), Math
                        .round(mContentHeight * zoomScale));
                tx += mScrollX;
                ty += mScrollY;
            }
            canvas.translate(tx, ty);
            canvas.scale(zoomScale, zoomScale);
        } else {
            canvas.scale(mActualScale, mActualScale);
        }

        mWebViewCore.drawContentPicture(canvas, color, animateZoom,
                animateScroll);

        if (mNativeClass == 0) return;
        if (mShiftIsPressed) {
            if (mTouchSelection) {
                nativeDrawSelectionRegion(canvas);
            } else {
                nativeDrawSelection(canvas, mSelectX, mSelectY, 
                        mExtendSelection);
            }
        } else if (drawFocus) {
            if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
                mTouchMode = TOUCH_SHORTPRESS_MODE;
                HitTestResult hitTest = getHitTestResult();
                if (hitTest != null &&
                        hitTest.mType != HitTestResult.UNKNOWN_TYPE) {
                    mPrivateHandler.sendMessageDelayed(mPrivateHandler
                            .obtainMessage(SWITCH_TO_LONGPRESS),
                            LONG_PRESS_TIMEOUT);
                }
            }
            nativeDrawFocusRing(canvas);
        }
        // When the FindDialog is up, only draw the matches if we are not in
        // the process of scrolling them into view.
        if (mFindIsUp && !animateScroll) {
            nativeDrawMatches(canvas);
        }
    
booleandrawHistory()


    // Only check the flag, can be called from WebCore thread
      
        return mDrawHistory;
    
private voiddrawMagnifyFrame(android.graphics.Canvas canvas, android.graphics.Rect frame, android.graphics.Paint paint)

        final float ADORNMENT_LEN = 16.0f;
        float width = frame.width();
        float height = frame.height();
        Path path = new Path();
        path.moveTo(-ADORNMENT_LEN, -ADORNMENT_LEN);
        path.lineTo(0, 0);
        path.lineTo(width, 0);
        path.lineTo(width + ADORNMENT_LEN, -ADORNMENT_LEN);
        path.moveTo(-ADORNMENT_LEN, height + ADORNMENT_LEN);
        path.lineTo(0, height);
        path.lineTo(width, height);
        path.lineTo(width + ADORNMENT_LEN, height + ADORNMENT_LEN);
        path.moveTo(0, 0);
        path.lineTo(0, height);
        path.moveTo(width, 0);
        path.lineTo(width, height);
        path.offset(frame.left, frame.top);
        canvas.drawPath(path, paint);
    
public voidemulateShiftHeld()

hide

        mExtendSelection = false;
        mShiftIsPressed = true;
        int contentX = viewToContent((int) mLastTouchX + mScrollX);
        int contentY = viewToContent((int) mLastTouchY + mScrollY);
        nativeClearFocus(contentX, contentY);
    
public static voidenablePlatformNotifications()
Enables platform notifications of data state and proxy changes.

        Network.enablePlatformNotifications();
    
private booleanextendScroll(int y)

        int finalY = mScroller.getFinalY();
        int newY = pinLocY(finalY + y);
        if (newY == finalY) return false;
        mScroller.setFinalY(newY);
        mScroller.extendDuration(computeDuration(0, y));
        return true;
    
public voidexternalRepresentation(android.os.Message callback)
{@hide}

        mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
    
protected voidfinalize()

        destroy();
    
public static java.lang.StringfindAddress(java.lang.String addr)
Return the first substring consisting of the address of a physical location. Currently, only addresses in the United States are detected, and consist of: - a house number - a street name - a street type (Road, Circle, etc), either spelled out or abbreviated - a city name - a state or territory, either spelled out or two-letter abbr. - an optional 5 digit or 9 digit zip code. All names must be correctly capitalized, and the zip code, if present, must be valid for the state. The street type must be a standard USPS spelling or abbreviation. The state or territory must also be spelled or abbreviated using USPS standards. The house number may not exceed five digits.

param
addr The string to search for addresses.
return
the address, or if no address is found, return null.

        return WebViewCore.nativeFindAddress(addr);
    
public intfindAll(java.lang.String find)

        mFindIsUp = true;
        int result = nativeFindAll(find.toLowerCase(), find.toUpperCase());
        invalidate();
        return result;
    
public voidfindNext(boolean forward)

        nativeFindNext(forward);
    
public voidflingScroll(int vx, int vy)

        int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
        int maxY = Math.max(computeVerticalScrollRange() - getViewHeight(), 0);
        
        mScroller.fling(mScrollX, mScrollY, vx, vy, 0, maxX, 0, maxY);
        invalidate();
    
public android.net.http.SslCertificategetCertificate()

return
The SSL certificate for the main top-level page or null if there is no certificate (the site is not secure).

        return mCertificate;
    
public intgetContentHeight()

return
the height of the HTML content.

        return mContentHeight;
    
public android.graphics.BitmapgetFavicon()
Get the favicon for the current page. This is the favicon of the current page until WebViewClient.onReceivedIcon is called.

return
The favicon for the current page.

        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
        return h != null ? h.getFavicon() : null;
    
public android.webkit.WebView$HitTestResultgetHitTestResult()
Return a HitTestResult based on the current focus node. If a HTML::a tag is found and the anchor has a non-javascript url, the HitTestResult type is set to SRC_ANCHOR_TYPE and the url is set in the "extra" field. If the anchor does not have a url or if it is a javascript url, the type will be UNKNOWN_TYPE and the url has to be retrieved through {@link #requestFocusNodeHref} asynchronously. If a HTML::img tag is found, the HitTestResult type is set to IMAGE_TYPE and the url is set in the "extra" field. A type of SRC_IMAGE_ANCHOR_TYPE indicates an anchor with a url that has an image as a child node. If a phone number is found, the HitTestResult type is set to PHONE_TYPE and the phone number is set in the "extra" field of HitTestResult. If a map address is found, the HitTestResult type is set to GEO_TYPE and the address is set in the "extra" field of HitTestResult. If an email address is found, the HitTestResult type is set to EMAIL_TYPE and the email is set in the "extra" field of HitTestResult. Otherwise, HitTestResult type is set to UNKNOWN_TYPE.

        if (mNativeClass == 0) {
            return null;
        }

        HitTestResult result = new HitTestResult();

        if (nativeUpdateFocusNode()) {
            FocusNode node = mFocusNode;
            if (node.mIsTextField || node.mIsTextArea) {
                result.setType(HitTestResult.EDIT_TEXT_TYPE);
            } else if (node.mText != null) {
                String text = node.mText;
                if (text.startsWith(SCHEME_TEL)) {
                    result.setType(HitTestResult.PHONE_TYPE);
                    result.setExtra(text.substring(SCHEME_TEL.length()));
                } else if (text.startsWith(SCHEME_MAILTO)) {
                    result.setType(HitTestResult.EMAIL_TYPE);
                    result.setExtra(text.substring(SCHEME_MAILTO.length()));
                } else if (text.startsWith(SCHEME_GEO)) {
                    result.setType(HitTestResult.GEO_TYPE);
                    result.setExtra(URLDecoder.decode(text
                            .substring(SCHEME_GEO.length())));
                } else if (node.mIsAnchor) {
                    result.setType(HitTestResult.SRC_ANCHOR_TYPE);
                    result.setExtra(text);
                }
            }
        }
        int type = result.getType();
        if (type == HitTestResult.UNKNOWN_TYPE
                || type == HitTestResult.SRC_ANCHOR_TYPE) {
            // Now check to see if it is an image.
            int contentX = viewToContent((int) mLastTouchX + mScrollX);
            int contentY = viewToContent((int) mLastTouchY + mScrollY);
            String text = nativeImageURI(contentX, contentY);
            if (text != null) {
                result.setType(type == HitTestResult.UNKNOWN_TYPE ? 
                        HitTestResult.IMAGE_TYPE : 
                        HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
                result.setExtra(text);
            }
        }
        return result;
    
public java.lang.String[]getHttpAuthUsernamePassword(java.lang.String host, java.lang.String realm)
Retrieve the HTTP authentication username and password for a given host & realm pair

param
host The host for which the credentials apply.
param
realm The realm for which the credentials apply.
return
String[] if found, String[0] is username, which can be null and String[1] is password. Return null if it can't find anything.

        return mDatabase.getHttpAuthUsernamePassword(host, realm);
    
public java.lang.StringgetOriginalUrl()
Get the original url for the current page. This is not always the same as the url passed to WebViewClient.onPageStarted because although the load for that url has begun, the current page may not have changed. Also, there may have been redirects resulting in a different url to that originally requested.

return
The url that was originally requested for the current page.

        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
        return h != null ? h.getOriginalUrl() : null;
    
public static synchronized PluginListgetPluginList()
Return the list of currently loaded plugins.

return
The list of currently loaded plugins.

        if (sPluginList == null) {
            sPluginList = new PluginList();
        }
        return sPluginList;
    
public intgetProgress()
Get the progress for the current page.

return
The progress for the current page between 0 and 100.

        return mCallbackProxy.getProgress();
    
public floatgetScale()
Return the current scale of the WebView

return
The current scale.

        return mActualScale;
    
private intgetScaledMaxXScroll()

        int width;
        if (mHeightCanMeasure == false) {
            width = getViewWidth() / 4;
        } else {
            Rect visRect = new Rect();
            calcOurVisibleRect(visRect);
            width = visRect.width() / 2;
        }
        // FIXME the divisor should be retrieved from somewhere
        return viewToContent(width);
    
private intgetScaledMaxYScroll()

        int height;
        if (mHeightCanMeasure == false) {
            height = getViewHeight() / 4;
        } else {
            Rect visRect = new Rect();
            calcOurVisibleRect(visRect);
            height = visRect.height() / 2;
        }
        // FIXME the divisor should be retrieved from somewhere
        // the closest thing today is hard-coded into ScrollView.java
        // (from ScrollView.java, line 363)   int maxJump = height/2;
        return viewToContent(height);
    
public WebSettingsgetSettings()
Return the WebSettings object used to control the settings for this WebView.

return
A WebSettings object that can be used to control this WebView's settings.

        return mWebViewCore.getSettings();
    
public java.lang.StringgetTitle()
Get the title for the current page. This is the title of the current page until WebViewClient.onReceivedTitle is called.

return
The title for the current page.

        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
        return h != null ? h.getTitle() : null;
    
public java.lang.StringgetUrl()
Get the url for the current page. This is not always the same as the url passed to WebViewClient.onPageStarted because although the load for that url has begun, the current page may not have changed.

return
The url for the current page.

        WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
        return h != null ? h.getUrl() : null;
    
private intgetViewHeight()

        if (!isHorizontalScrollBarEnabled() || mOverlayHorizontalScrollbar) {
            return getHeight();
        } else {
            return getHeight() - getHorizontalScrollbarHeight();
        }
    
private intgetViewWidth()

        if (!isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
            return getWidth();
        } else {
            return getWidth() - getVerticalScrollbarWidth();
        }
    
WebViewCoregetWebViewCore()

        return mWebViewCore;
    
public android.widget.ZoomButtonsControllergetZoomButtonsController()
Gets the {@link ZoomButtonsController} which can be used to add additional buttons to the zoom controls window.

return
The instance of {@link ZoomButtonsController} used by this class, or null if it is unavailable.
hide

        return mZoomButtonsController;
    
public android.view.ViewgetZoomControls()
Returns a view containing zoom controls i.e. +/- buttons. The caller is in charge of installing this view to the view hierarchy. This view will become visible when the user starts scrolling via touch and fade away if the user does not interact with it.

API version 3 introduces a built-in zoom mechanism that is shown automatically by the MapView. This is the preferred approach for showing the zoom UI.

deprecated
The built-in zoom mechanism is preferred, see {@link WebSettings#setBuiltInZoomControls(boolean)}.

        if (!getSettings().supportZoom()) {
            Log.w(LOGTAG, "This WebView doesn't support zoom.");
            return null;
        }
        if (mZoomControls == null) {
            mZoomControls = createZoomControls();
            
            /*
             * need to be set to VISIBLE first so that getMeasuredHeight() in
             * {@link #onSizeChanged()} can return the measured value for proper
             * layout.
             */
            mZoomControls.setVisibility(View.VISIBLE);
            mZoomControlRunnable = new Runnable() {
                public void run() {
                    
                    /* Don't dismiss the controls if the user has
                     * focus on them. Wait and check again later.
                     */
                    if (!mZoomControls.hasFocus()) {
                        mZoomControls.hide();
                    } else {
                        mPrivateHandler.removeCallbacks(mZoomControlRunnable);
                        mPrivateHandler.postDelayed(mZoomControlRunnable,
                                ZOOM_CONTROLS_TIMEOUT);
                    }
                }
            };
        }
        return mZoomControls;
    
public voidgoBack()
Go back in the history of this WebView.

        goBackOrForward(-1);
    
public voidgoBackOrForward(int steps)
Go to the history item that is the number of steps away from the current item. Steps is negative if backward and positive if forward.

param
steps The number of steps to take back or forward in the back forward list.

        goBackOrForward(steps, false);
    
private voidgoBackOrForward(int steps, boolean ignoreSnapshot)

        // every time we go back or forward, we want to reset the
        // WebView certificate:
        // if the new site is secure, we will reload it and get a
        // new certificate set;
        // if the new site is not secure, the certificate must be
        // null, and that will be the case
        mCertificate = null;
        if (steps != 0) {
            clearTextEntry();
            mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
                    ignoreSnapshot ? 1 : 0);
        }
    
public voidgoForward()
Go forward in the history of this WebView.

        goBackOrForward(1);
    
private booleaninEditingMode()
Return true if the browser is displaying a TextView for text input.

        return mTextEntry != null && mTextEntry.getParent() != null
                && mTextEntry.hasFocus();
    
private voidinit()

        setWillNotDraw(false);
        setFocusable(true);
        setFocusableInTouchMode(true);
        setClickable(true);
        setLongClickable(true);

        final int slop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
        mTouchSlopSquare = slop * slop;
        mMinLockSnapReverseDistance = slop;
        // use one line height, 16 based on our current default font, for how
        // far we allow a touch be away from the edge of a link
        mNavSlop = (int) (16 * getContext().getResources()
                .getDisplayMetrics().density);
    
private voidinitZoomController(android.content.Context context)

        // Create the buttons controller
        mZoomButtonsController = new ZoomButtonsController(this);
        mZoomButtonsController.setOnZoomListener(mZoomListener);

        // Create the accessory buttons
        LayoutInflater inflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        ViewGroup container = mZoomButtonsController.getContainer();
        inflater.inflate(com.android.internal.R.layout.zoom_browser_accessory_buttons, container);
        mZoomOverviewButton =
                (ImageView) container.findViewById(com.android.internal.R.id.zoom_page_overview);
        mZoomOverviewButton.setOnClickListener(
            new View.OnClickListener() {
                public void onClick(View v) {
                    mZoomButtonsController.setVisible(false);
                    zoomScrollOut();
                    if (mLogEvent) {
                        Checkin.updateStats(mContext.getContentResolver(),
                                Checkin.Stats.Tag.BROWSER_ZOOM_OVERVIEW, 1, 0.0);
                    }
                }
            });
        mZoomFitPageButton =
                (ImageView) container.findViewById(com.android.internal.R.id.zoom_fit_page);
        mZoomFitPageButton.setOnClickListener(
            new View.OnClickListener() {
                public void onClick(View v) {
                    zoomWithPreview(1f);
                    updateZoomButtonsEnabled();
                }
            });
    
public voidinvokeZoomPicker()
Invoke the graphical zoom picker widget for this WebView. This will result in the zoom widget appearing on the screen to control the zoom level of this WebView.

        if (!getSettings().supportZoom()) {
            Log.w(LOGTAG, "This WebView doesn't support zoom.");
            return;
        }
        clearTextEntry();
        if (getSettings().getBuiltInZoomControls()) {
            mZoomButtonsController.setVisible(true);
        } else {
            mPrivateHandler.removeCallbacks(mZoomControlRunnable);
            mPrivateHandler.postDelayed(mZoomControlRunnable,
                    ZOOM_CONTROLS_TIMEOUT);
        }
    
private intkeyCodeToSoundsEffect(int keyCode)

        switch(keyCode) {
            case KeyEvent.KEYCODE_DPAD_UP:
                return SoundEffectConstants.NAVIGATION_UP;
            case KeyEvent.KEYCODE_DPAD_RIGHT:
                return SoundEffectConstants.NAVIGATION_RIGHT;
            case KeyEvent.KEYCODE_DPAD_DOWN:
                return SoundEffectConstants.NAVIGATION_DOWN;
            case KeyEvent.KEYCODE_DPAD_LEFT:
                return SoundEffectConstants.NAVIGATION_LEFT;
        }
        throw new IllegalArgumentException("keyCode must be one of " +
                "{KEYCODE_DPAD_UP, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_DOWN, " +
                "KEYCODE_DPAD_LEFT}.");
    
public voidloadData(java.lang.String data, java.lang.String mimeType, java.lang.String encoding)
Load the given data into the WebView. This will load the data into WebView using the data: scheme. Content loaded through this mechanism does not have the ability to load content from the network.

param
data A String of data in the given encoding.
param
mimeType The MIMEType of the data. i.e. text/html, image/jpeg
param
encoding The encoding of the data. i.e. utf-8, base64

        loadUrl("data:" + mimeType + ";" + encoding + "," + data);
    
public voidloadDataWithBaseURL(java.lang.String baseUrl, java.lang.String data, java.lang.String mimeType, java.lang.String encoding, java.lang.String failUrl)
Load the given data into the WebView, use the provided URL as the base URL for the content. The base URL is the URL that represents the page that is loaded through this interface. As such, it is used for the history entry and to resolve any relative URLs. The failUrl is used if browser fails to load the data provided. If it is empty or null, and the load fails, then no history entry is created.

Note for post 1.0. Due to the change in the WebKit, the access to asset files through "file:///android_asset/" for the sub resources is more restricted. If you provide null or empty string as baseUrl, you won't be able to access asset files. If the baseUrl is anything other than http(s)/ftp(s)/about/javascript as scheme, you can access asset files for sub resources.

param
baseUrl Url to resolve relative paths with, if null defaults to "about:blank"
param
data A String of data in the given encoding.
param
mimeType The MIMEType of the data. i.e. text/html. If null, defaults to "text/html"
param
encoding The encoding of the data. i.e. utf-8, us-ascii
param
failUrl URL to use if the content fails to load or null.

        
        if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
            loadData(data, mimeType, encoding);
            return;
        }
        switchOutDrawHistory();
        HashMap arg = new HashMap();
        arg.put("baseUrl", baseUrl);
        arg.put("data", data);
        arg.put("mimeType", mimeType);
        arg.put("encoding", encoding);
        arg.put("failUrl", failUrl);
        mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
        clearTextEntry();
    
public voidloadUrl(java.lang.String url)
Load the given url.

param
url The url of the resource to load.

        switchOutDrawHistory();
        mWebViewCore.sendMessage(EventHub.LOAD_URL, url);
        clearTextEntry();
    
voidmoveSelection(float xRate, float yRate)

        if (mNativeClass == 0)
            return;
        int width = getViewWidth();
        int height = getViewHeight();
        mSelectX += scaleTrackballX(xRate, width);
        mSelectY += scaleTrackballY(yRate, height);
        int maxX = width + mScrollX;
        int maxY = height + mScrollY;
        mSelectX = Math.min(maxX, Math.max(mScrollX - SELECT_CURSOR_OFFSET
                , mSelectX));
        mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET
                , mSelectY));
        if (LOGV_ENABLED) {
            Log.v(LOGTAG, "moveSelection" 
                    + " mSelectX=" + mSelectX
                    + " mSelectY=" + mSelectY
                    + " mScrollX=" + mScrollX
                    + " mScrollY=" + mScrollY
                    + " xRate=" + xRate
                    + " yRate=" + yRate
                    );
        }
        nativeMoveSelection(viewToContent(mSelectX)
                , viewToContent(mSelectY), mExtendSelection);
        int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
                : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET 
                : 0;
        int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET
                : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET 
                : 0;
        pinScrollBy(scrollX, scrollY, true, 0);
        Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1);
        requestRectangleOnScreen(select);
        invalidate();
   
private voidmoveZoomScrollWindow(float x, float y)

        if (Math.abs(x - mLastZoomScrollRawX) < 1.5f 
                && Math.abs(y - mLastZoomScrollRawY) < 1.5f) {
            return;
        }
        mLastZoomScrollRawX = x;
        mLastZoomScrollRawY = y;
        int oldX = mZoomScrollX;
        int oldY = mZoomScrollY;
        int width = getViewWidth();
        int height = getViewHeight();
        int maxZoomX = mContentWidth - width;
        if (maxZoomX > 0) {
            int maxScreenX = width - (int) Math.ceil(width 
                    * mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
            if (LOGV_ENABLED) {
                Log.v(LOGTAG, "moveZoomScrollWindow-X" 
                        + " maxScreenX=" + maxScreenX + " width=" + width
                        + " mZoomScrollLimit=" + mZoomScrollLimit + " x=" + x); 
            }
            x += maxScreenX * mLastScrollX / maxZoomX - mLastTouchX;
            x *= Math.max(maxZoomX / maxScreenX, mZoomScrollInvLimit);
            mZoomScrollX = Math.max(0, Math.min(maxZoomX, (int) x));
        }
        int maxZoomY = mContentHeight - height;
        if (maxZoomY > 0) {
            int maxScreenY = height - (int) Math.ceil(height 
                    * mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
            if (LOGV_ENABLED) {
                Log.v(LOGTAG, "moveZoomScrollWindow-Y" 
                        + " maxScreenY=" + maxScreenY + " height=" + height
                        + " mZoomScrollLimit=" + mZoomScrollLimit + " y=" + y); 
            }
            y += maxScreenY * mLastScrollY / maxZoomY - mLastTouchY;
            y *= Math.max(maxZoomY / maxScreenY, mZoomScrollInvLimit);
            mZoomScrollY = Math.max(0, Math.min(maxZoomY, (int) y));
        }
        if (oldX != mZoomScrollX || oldY != mZoomScrollY) {
            invalidate();
        }
        if (LOGV_ENABLED) {
            Log.v(LOGTAG, "moveZoomScrollWindow" 
                    + " scrollTo=(" + mZoomScrollX + ", " + mZoomScrollY + ")" 
                    + " mLastTouch=(" + mLastTouchX + ", " + mLastTouchY + ")" 
                    + " maxZoom=(" + maxZoomX + ", " + maxZoomY + ")" 
                    + " last=("+mLastScrollX+", "+mLastScrollY+")" 
                    + " x=" + x + " y=" + y);
        }
    
private native voidnativeClearFocus(int x, int y)

private native voidnativeCreate(int ptr)

private native voidnativeDebugDump()

private native voidnativeDestroy()

private native voidnativeDrawFocusRing(android.graphics.Canvas content)

private native voidnativeDrawMatches(android.graphics.Canvas canvas)

private native voidnativeDrawSelection(android.graphics.Canvas content, int x, int y, boolean extendSelection)

private native voidnativeDrawSelectionRegion(android.graphics.Canvas content)

private native voidnativeDumpDisplayTree(java.lang.String urlOrNull)

private native intnativeFindAll(java.lang.String findLower, java.lang.String findUpper)

private native voidnativeFindNext(boolean forward)

private native booleannativeFocusNodeWantsKeyEvents()
Returns true if the native focus nodes says it wants to handle key events (ala plugins). This can only be called if mNativeClass is non-zero!

private native android.graphics.RectnativeGetFocusRingBounds()

private native android.graphics.RectnativeGetNavBounds()

private native android.graphics.RegionnativeGetSelection()

private native java.lang.StringnativeImageURI(int x, int y)

private native voidnativeInstrumentReport()

private native voidnativeMarkNodeInvalid(int node)

private native booleannativeMotionUp(int x, int y, int slop, boolean isClick)

private native booleannativeMoveFocus(int keyCode, int count, boolean noScroll)

private native voidnativeMoveSelection(int x, int y, boolean extendSelection)

private native voidnativeNotifyFocusSet(boolean inEditingMode)

private native voidnativeRecomputeFocus()

private native voidnativeRecordButtons(boolean focused, boolean pressed, boolean invalidate)

private native voidnativeResetFocus()

private native voidnativeResetNavClipBounds()

private native voidnativeSelectBestAt(android.graphics.Rect rect)

private native voidnativeSetFindIsDown()

private native voidnativeSetFollowedLink(boolean followed)

private native voidnativeSetHeightCanMeasure(boolean measure)

private native voidnativeSetNavBounds(android.graphics.Rect rect)

private native voidnativeSetNavClipBounds(android.graphics.Rect rect)

private native voidnativeUpdateCachedTextfield(java.lang.String updatedText, int generation)

private native booleannativeUpdateFocusNode()

private booleannavHandledKey(int keyCode, int count, boolean noScroll, long time)

        if (mNativeClass == 0) {
            return false;
        }
        mLastFocusTime = time;
        mLastFocusBounds = nativeGetFocusRingBounds();
        boolean keyHandled = nativeMoveFocus(keyCode, count, noScroll) == false;
        if (LOGV_ENABLED) {
            Log.v(LOGTAG, "navHandledKey mLastFocusBounds=" + mLastFocusBounds
                    + " mLastFocusTime=" + mLastFocusTime
                    + " handled=" + keyHandled);
        }
        if (keyHandled == false || mHeightCanMeasure == false) {
            return keyHandled;
        }
        Rect contentFocus = nativeGetFocusRingBounds();
        if (contentFocus.isEmpty()) return keyHandled;
        Rect viewFocus = contentToView(contentFocus);
        Rect visRect = new Rect();
        calcOurVisibleRect(visRect);
        Rect outset = new Rect(visRect);
        int maxXScroll = visRect.width() / 2;
        int maxYScroll = visRect.height() / 2;
        outset.inset(-maxXScroll, -maxYScroll);
        if (Rect.intersects(outset, viewFocus) == false) {
            return keyHandled;
        }
        // FIXME: Necessary because ScrollView/ListView do not scroll left/right
        int maxH = Math.min(viewFocus.right - visRect.right, maxXScroll);
        if (maxH > 0) {
            pinScrollBy(maxH, 0, true, 0);
        } else {
            maxH = Math.max(viewFocus.left - visRect.left, -maxXScroll);
            if (maxH < 0) {
                pinScrollBy(maxH, 0, true, 0);
            }
        }
        if (mLastFocusBounds.isEmpty()) return keyHandled;
        if (mLastFocusBounds.equals(contentFocus)) return keyHandled;
        if (LOGV_ENABLED) {
            Log.v(LOGTAG, "navHandledKey contentFocus=" + contentFocus);
        }
        requestRectangleOnScreen(viewFocus);
        mUserScroll = true;
        return keyHandled;
    
protected voidonAttachedToWindow()

        super.onAttachedToWindow();
        ViewParent parent = getParent();
        if (parent instanceof ViewGroup) {
            ViewGroup p = (ViewGroup) parent;
            p.setOnHierarchyChangeListener(this);
        }
    
public voidonChildViewAdded(android.view.View parent, android.view.View child)

public voidonChildViewRemoved(android.view.View p, android.view.View child)

        if (child == this) {
            if (inEditingMode()) {
                clearTextEntry();
                mNeedsUpdateTextEntry = true;
            }
        }
    
protected voidonDetachedFromWindow()

        super.onDetachedFromWindow();
        ViewParent parent = getParent();
        if (parent instanceof ViewGroup) {
            ViewGroup p = (ViewGroup) parent;
            p.setOnHierarchyChangeListener(null);
        }

        // Clean up the zoom controller
        mZoomButtonsController.setVisible(false);
    
protected voidonDraw(android.graphics.Canvas canvas)

        // if mNativeClass is 0, the WebView has been destroyed. Do nothing.
        if (mNativeClass == 0) {
            return;
        }
        if (mWebViewCore.mEndScaleZoom) {
            mWebViewCore.mEndScaleZoom = false;
            if (mTouchMode >= FIRST_SCROLL_ZOOM 
                    && mTouchMode <= LAST_SCROLL_ZOOM) {
                setHorizontalScrollBarEnabled(true);
                setVerticalScrollBarEnabled(true);
                mTouchMode = TOUCH_DONE_MODE;
            }
        }
        int sc = canvas.save();
        if (mTouchMode >= FIRST_SCROLL_ZOOM && mTouchMode <= LAST_SCROLL_ZOOM) {
            scrollZoomDraw(canvas);
        } else {
            nativeRecomputeFocus();
            // Update the buttons in the picture, so when we draw the picture
            // to the screen, they are in the correct state.
            // Tell the native side if user is a) touching the screen,
            // b) pressing the trackball down, or c) pressing the enter key
            // If the focus is a button, we need to draw it in the pressed
            // state.
            // If mNativeClass is 0, we should not reach here, so we do not
            // need to check it again.
            nativeRecordButtons(hasFocus() && hasWindowFocus(),
                    mTouchMode == TOUCH_SHORTPRESS_START_MODE
                    || mTrackballDown || mGotEnterDown, false);
            drawCoreAndFocusRing(canvas, mBackgroundColor, mDrawFocusRing);
        }
        canvas.restoreToCount(sc);
        
        if (AUTO_REDRAW_HACK && mAutoRedraw) {
            invalidate();
        }
    
protected voidonFocusChanged(boolean focused, int direction, android.graphics.Rect previouslyFocusedRect)

        if (LOGV_ENABLED) {
            Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
        }
        if (focused) {
            // When we regain focus, if we have window focus, resume drawing
            // the focus ring, and add the TextView if necessary.
            if (hasWindowFocus()) {
                mDrawFocusRing = true;
                if (mNeedsUpdateTextEntry) {
                    updateTextEntry();
                    mNeedsUpdateTextEntry = false;
                }
                if (mNativeClass != 0) {
                    nativeRecordButtons(true, false, true);
                }
            //} else {
                // The WebView has gained focus while we do not have
                // windowfocus.  When our window lost focus, we should have
                // called nativeRecordButtons(false...)
            }
        } else {
            // When we lost focus, unless focus went to the TextView (which is
            // true if we are in editing mode), stop drawing the focus ring.
            if (!inEditingMode()) {
                mDrawFocusRing = false;
                if (mNativeClass != 0) {
                    nativeRecordButtons(false, false, true);
                }
            }
            mGotKeyDown = false;
        }

        super.onFocusChanged(focused, direction, previouslyFocusedRect);
    
public voidonGlobalFocusChanged(android.view.View oldFocus, android.view.View newFocus)

deprecated
WebView should not have implemented ViewTreeObserver.OnGlobalFocusChangeListener. This method does nothing now.

    
public booleanonKeyDown(int keyCode, android.view.KeyEvent event)


    
          
        if (LOGV_ENABLED) {
            Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
                    + ", " + event);
        }

        if (mNativeClass == 0) {
            return false;
        }

        // do this hack up front, so it always works, regardless of touch-mode
        if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) {
            mAutoRedraw = !mAutoRedraw;
            if (mAutoRedraw) {
                invalidate();
            }
            return true;
        }

        // Bubble up the key event if
        // 1. it is a system key; or
        // 2. the host application wants to handle it; or
        // 3. webview is in scroll-zoom state;
        if (event.isSystem()
                || mCallbackProxy.uiOverrideKeyEvent(event)
                || (mTouchMode >= FIRST_SCROLL_ZOOM && mTouchMode <= LAST_SCROLL_ZOOM)) {
            return false;
        }

        if (mShiftIsPressed == false && nativeFocusNodeWantsKeyEvents() == false
                && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT 
                || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)) {
            mExtendSelection = false;
            mShiftIsPressed = true;
            if (nativeUpdateFocusNode()) {
                FocusNode node = mFocusNode;
                mSelectX = contentToView(node.mBounds.left);
                mSelectY = contentToView(node.mBounds.top);
            } else {
                mSelectX = mScrollX + (int) mLastTouchX;
                mSelectY = mScrollY + (int) mLastTouchY;
            }
            int contentX = viewToContent((int) mLastTouchX + mScrollX);
            int contentY = viewToContent((int) mLastTouchY + mScrollY);
            nativeClearFocus(contentX, contentY);
       }

        if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
                && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
            // always handle the navigation keys in the UI thread
            switchOutDrawHistory();
            if (navHandledKey(keyCode, 1, false, event.getEventTime())) {
                playSoundEffect(keyCodeToSoundsEffect(keyCode));
                return true;
            }
            // Bubble up the key event as WebView doesn't handle it
            return false;
        }

        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
                || keyCode == KeyEvent.KEYCODE_ENTER) {
            switchOutDrawHistory();
            if (event.getRepeatCount() == 0) {
                mGotEnterDown = true;
                mPrivateHandler.sendMessageDelayed(mPrivateHandler
                        .obtainMessage(LONG_PRESS_ENTER), LONG_PRESS_TIMEOUT);
                // Already checked mNativeClass, so we do not need to check it
                // again.
                nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
                return true;
            }
            // Bubble up the key event as WebView doesn't handle it
            return false;
        }

        if (getSettings().getNavDump()) {
            switch (keyCode) {
                case KeyEvent.KEYCODE_4:
                    // "/data/data/com.android.browser/displayTree.txt"
                    nativeDumpDisplayTree(getUrl());
                    break;
                case KeyEvent.KEYCODE_5:
                case KeyEvent.KEYCODE_6:
                    // 5: dump the dom tree to the file
                    // "/data/data/com.android.browser/domTree.txt"
                    // 6: dump the dom tree to the adb log
                    mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE,
                            (keyCode == KeyEvent.KEYCODE_5) ? 1 : 0, 0);
                    break;
                case KeyEvent.KEYCODE_7:
                case KeyEvent.KEYCODE_8:
                    // 7: dump the render tree to the file
                    // "/data/data/com.android.browser/renderTree.txt"
                    // 8: dump the render tree to the adb log
                    mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE,
                            (keyCode == KeyEvent.KEYCODE_7) ? 1 : 0, 0);
                    break;
                case KeyEvent.KEYCODE_9:
                    nativeInstrumentReport();
                    return true;
            }
        }

        // TODO: should we pass all the keys to DOM or check the meta tag
        if (nativeFocusNodeWantsKeyEvents() || true) {
            // pass the key to DOM
            mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
            // return true as DOM handles the key
            return true;
        }

        // Bubble up the key event as WebView doesn't handle it
        return false;
    
public booleanonKeyUp(int keyCode, android.view.KeyEvent event)

        if (LOGV_ENABLED) {
            Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
                    + ", " + event);
        }

        if (mNativeClass == 0) {
            return false;
        }

        // special CALL handling when focus node's href is "tel:XXX"
        if (keyCode == KeyEvent.KEYCODE_CALL && nativeUpdateFocusNode()) {
            FocusNode node = mFocusNode;
            String text = node.mText;
            if (!node.mIsTextField && !node.mIsTextArea && text != null
                    && text.startsWith(SCHEME_TEL)) {
                Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
                getContext().startActivity(intent);
                return true;
            }
        }

        // Bubble up the key event if
        // 1. it is a system key; or
        // 2. the host application wants to handle it;
        if (event.isSystem() || mCallbackProxy.uiOverrideKeyEvent(event)) {
            return false;
        }

        // special handling in scroll_zoom state
        if (mTouchMode >= FIRST_SCROLL_ZOOM && mTouchMode <= LAST_SCROLL_ZOOM) {
            if (KeyEvent.KEYCODE_DPAD_CENTER == keyCode
                    && mTouchMode != SCROLL_ZOOM_ANIMATION_IN) {
                setZoomScrollIn();
                mTouchMode = SCROLL_ZOOM_ANIMATION_IN;
                invalidate();
                return true;
            }
            return false;
        }

        if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT 
                || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
            if (commitCopy()) {
                return true;
            }
        }

        if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
                && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
            // always handle the navigation keys in the UI thread
            // Bubble up the key event as WebView doesn't handle it
            return false;
        }

        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
                || keyCode == KeyEvent.KEYCODE_ENTER) {
            // remove the long press message first
            mPrivateHandler.removeMessages(LONG_PRESS_ENTER);
            mGotEnterDown = false;

            if (KeyEvent.KEYCODE_DPAD_CENTER == keyCode) {
                if (mShiftIsPressed) {
                    return false;
                }
                if (getSettings().supportZoom()) {
                    if (mTouchMode == TOUCH_DOUBLECLICK_MODE) {
                        zoomScrollOut();
                    } else {
                        if (LOGV_ENABLED) {
                            Log.v(LOGTAG, "TOUCH_DOUBLECLICK_MODE");
                        }
                        mPrivateHandler.sendMessageDelayed(mPrivateHandler
                                .obtainMessage(SWITCH_TO_ENTER), TAP_TIMEOUT);
                        mTouchMode = TOUCH_DOUBLECLICK_MODE;
                    }
                    return true;
                }
            }

            Rect visibleRect = sendOurVisibleRect();
            // Note that sendOurVisibleRect calls viewToContent, so the
            // coordinates should be in content coordinates.
            if (nativeUpdateFocusNode()) {
                if (Rect.intersects(mFocusNode.mBounds, visibleRect)) {
                    nativeSetFollowedLink(true);
                    mWebViewCore.sendMessage(EventHub.SET_FINAL_FOCUS,
                            EventHub.BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP, 0,
                            new WebViewCore.FocusData(mFocusData));
                    playSoundEffect(SoundEffectConstants.CLICK);
                    if (!mCallbackProxy.uiOverrideUrlLoading(mFocusNode.mText)) {
                        // use CLICK instead of KEY_DOWN/KEY_UP so that we can
                        // trigger mouse click events
                        mWebViewCore.sendMessage(EventHub.CLICK);
                    }
                }
                return true;
            }
            // Bubble up the key event as WebView doesn't handle it
            return false;
        }

        // TODO: should we pass all the keys to DOM or check the meta tag
        if (nativeFocusNodeWantsKeyEvents() || true) {
            // pass the key to DOM
            mWebViewCore.sendMessage(EventHub.KEY_UP, event);
            // return true as DOM handles the key
            return true;
        }

        // Bubble up the key event as WebView doesn't handle it
        return false;
    
protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec)

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        int measuredHeight = heightSize;
        int measuredWidth = widthSize;

        // Grab the content size from WebViewCore.
        int contentHeight = mContentHeight;
        int contentWidth = mContentWidth;

//        Log.d(LOGTAG, "------- measure " + heightMode);

        if (heightMode != MeasureSpec.EXACTLY) {
            mHeightCanMeasure = true;
            measuredHeight = contentHeight;
            if (heightMode == MeasureSpec.AT_MOST) {
                // If we are larger than the AT_MOST height, then our height can
                // no longer be measured and we should scroll internally.
                if (measuredHeight > heightSize) {
                    measuredHeight = heightSize;
                    mHeightCanMeasure = false;
                }
            }
        } else {
            mHeightCanMeasure = false;
        }
        if (mNativeClass != 0) {
            nativeSetHeightCanMeasure(mHeightCanMeasure);
        }
        // For the width, always use the given size unless unspecified.
        if (widthMode == MeasureSpec.UNSPECIFIED) {
            mWidthCanMeasure = true;
            measuredWidth = contentWidth;
        } else {
            mWidthCanMeasure = false;
        }

        synchronized (this) {
            setMeasuredDimension(measuredWidth, measuredHeight);
        }
    
booleanonSavePassword(java.lang.String schemePlusHost, java.lang.String username, java.lang.String password, android.os.Message resumeMsg)

       boolean rVal = false;
       if (resumeMsg == null) {
           // null resumeMsg implies saving password silently
           mDatabase.setUsernamePassword(schemePlusHost, username, password);
       } else {
            final Message remember = mPrivateHandler.obtainMessage(
                    REMEMBER_PASSWORD);
            remember.getData().putString("host", schemePlusHost);
            remember.getData().putString("username", username);
            remember.getData().putString("password", password);
            remember.obj = resumeMsg;

            final Message neverRemember = mPrivateHandler.obtainMessage(
                    NEVER_REMEMBER_PASSWORD);
            neverRemember.getData().putString("host", schemePlusHost);
            neverRemember.getData().putString("username", username);
            neverRemember.getData().putString("password", password);
            neverRemember.obj = resumeMsg;

            new AlertDialog.Builder(getContext())
                    .setTitle(com.android.internal.R.string.save_password_label)
                    .setMessage(com.android.internal.R.string.save_password_message)
                    .setPositiveButton(com.android.internal.R.string.save_password_notnow,
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            resumeMsg.sendToTarget();
                        }
                    })
                    .setNeutralButton(com.android.internal.R.string.save_password_remember,
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            remember.sendToTarget();
                        }
                    })
                    .setNegativeButton(com.android.internal.R.string.save_password_never,
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            neverRemember.sendToTarget();
                        }
                    })
                    .setOnCancelListener(new OnCancelListener() {
                        public void onCancel(DialogInterface dialog) {
                            resumeMsg.sendToTarget();
                        }
                    }).show();
            // Return true so that WebViewCore will pause while the dialog is
            // up.
            rVal = true;
        }
       return rVal;
    
protected voidonScrollChanged(int l, int t, int oldl, int oldt)

        super.onScrollChanged(l, t, oldl, oldt);
        sendOurVisibleRect();
    
protected voidonSizeChanged(int w, int h, int ow, int oh)

        super.onSizeChanged(w, h, ow, oh);
        // Center zooming to the center of the screen.
        mZoomCenterX = getViewWidth() * .5f;
        mZoomCenterY = getViewHeight() * .5f;

        // update mMinZoomScale if the minimum zoom scale is not fixed
        if (!mMinZoomScaleFixed) {
            mMinZoomScale = (float) getViewWidth()
                    / Math.max(ZOOM_OUT_WIDTH, mContentWidth);
        }

        // we always force, in case our height changed, in which case we still
        // want to send the notification over to webkit
        setNewZoomScale(mActualScale, true);
    
public booleanonTouchEvent(android.view.MotionEvent ev)


    
        
        if (mNativeClass == 0 || !isClickable() || !isLongClickable()) {
            return false;
        }

        if (LOGV_ENABLED) {
            Log.v(LOGTAG, ev + " at " + ev.getEventTime() + " mTouchMode="
                    + mTouchMode);
        }

        int action = ev.getAction();
        float x = ev.getX();
        float y = ev.getY();
        long eventTime = ev.getEventTime();

        // Due to the touch screen edge effect, a touch closer to the edge
        // always snapped to the edge. As getViewWidth() can be different from
        // getWidth() due to the scrollbar, adjusting the point to match
        // getViewWidth(). Same applied to the height.
        if (x > getViewWidth() - 1) {
            x = getViewWidth() - 1;
        }
        if (y > getViewHeight() - 1) {
            y = getViewHeight() - 1;
        }

        // pass the touch events from UI thread to WebCore thread
        if (mForwardTouchEvents && mTouchMode != SCROLL_ZOOM_OUT
                && mTouchMode != SCROLL_ZOOM_ANIMATION_IN
                && mTouchMode != SCROLL_ZOOM_ANIMATION_OUT
                && (action != MotionEvent.ACTION_MOVE || 
                        eventTime - mLastSentTouchTime > TOUCH_SENT_INTERVAL)) {
            WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
            ted.mAction = action;
            ted.mX = viewToContent((int) x + mScrollX);
            ted.mY = viewToContent((int) y + mScrollY);
            mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
            mLastSentTouchTime = eventTime;
        }

        int deltaX = (int) (mLastTouchX - x);
        int deltaY = (int) (mLastTouchY - y);

        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                if (mTouchMode == SCROLL_ZOOM_ANIMATION_IN
                        || mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
                    // no interaction while animation is in progress
                    break;
                } else if (mTouchMode == SCROLL_ZOOM_OUT) {
                    mLastScrollX = mZoomScrollX;
                    mLastScrollY = mZoomScrollY;
                    // If two taps are close, ignore the first tap
                } else if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                    mTouchMode = TOUCH_DRAG_START_MODE;
                    mPrivateHandler.removeMessages(RESUME_WEBCORE_UPDATE);
                } else if (mShiftIsPressed) {
                    mSelectX = mScrollX + (int) x;
                    mSelectY = mScrollY + (int) y;
                    mTouchMode = TOUCH_SELECT_MODE;
                    if (LOGV_ENABLED) {
                        Log.v(LOGTAG, "select=" + mSelectX + "," + mSelectY);
                    }
                    nativeMoveSelection(viewToContent(mSelectX)
                            , viewToContent(mSelectY), false);
                    mTouchSelection = mExtendSelection = true;
                } else {
                    mTouchMode = TOUCH_INIT_MODE;
                    mPreventDrag = mForwardTouchEvents;
                    if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
                        EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION,
                                (eventTime - mLastTouchUpTime), eventTime);
                    }
                }
                // Trigger the link
                if (mTouchMode == TOUCH_INIT_MODE) {
                    mPrivateHandler.sendMessageDelayed(mPrivateHandler
                            .obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT);
                }
                // Remember where the motion event started
                mLastTouchX = x;
                mLastTouchY = y;
                mLastTouchTime = eventTime;
                mVelocityTracker = VelocityTracker.obtain();
                mSnapScrollMode = SNAP_NONE;
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                if (mTouchMode == TOUCH_DONE_MODE 
                        || mTouchMode == SCROLL_ZOOM_ANIMATION_IN
                        || mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
                    // no dragging during scroll zoom animation
                    break;
                }
                if (mTouchMode == SCROLL_ZOOM_OUT) {
                    // while fully zoomed out, move the virtual window
                    moveZoomScrollWindow(x, y);
                    break;
                }
                mVelocityTracker.addMovement(ev);

                if (mTouchMode != TOUCH_DRAG_MODE) {
                    if (mTouchMode == TOUCH_SELECT_MODE) {
                        mSelectX = mScrollX + (int) x;
                        mSelectY = mScrollY + (int) y;
                        if (LOGV_ENABLED) {
                            Log.v(LOGTAG, "xtend=" + mSelectX + "," + mSelectY);
                        }
                        nativeMoveSelection(viewToContent(mSelectX)
                                , viewToContent(mSelectY), true);
                        invalidate();
                        break;
                    }
                    if (mPreventDrag || (deltaX * deltaX + deltaY * deltaY)
                            < mTouchSlopSquare) {
                        break;
                    }

                    if (mTouchMode == TOUCH_SHORTPRESS_MODE
                            || mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
                        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
                    } else if (mTouchMode == TOUCH_INIT_MODE) {
                        mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
                    }

                    // if it starts nearly horizontal or vertical, enforce it
                    int ax = Math.abs(deltaX);
                    int ay = Math.abs(deltaY);
                    if (ax > MAX_SLOPE_FOR_DIAG * ay) {
                        mSnapScrollMode = SNAP_X;
                        mSnapPositive = deltaX > 0;
                    } else if (ay > MAX_SLOPE_FOR_DIAG * ax) {
                        mSnapScrollMode = SNAP_Y;
                        mSnapPositive = deltaY > 0;
                    }

                    mTouchMode = TOUCH_DRAG_MODE;
                    WebViewCore.pauseUpdate(mWebViewCore);
                    int contentX = viewToContent((int) x + mScrollX);
                    int contentY = viewToContent((int) y + mScrollY);
                    if (inEditingMode()) {
                        mTextEntry.updateCachedTextfield();
                    }
                    nativeClearFocus(contentX, contentY);
                    // remove the zoom anchor if there is any
                    if (mZoomScale != 0) {
                        mWebViewCore
                                .sendMessage(EventHub.SET_SNAP_ANCHOR, 0, 0);
                    }
                    WebSettings settings = getSettings();
                    if (settings.supportZoom()
                            && settings.getBuiltInZoomControls()
                            && !mZoomButtonsController.isVisible()
                            && (canZoomScrollOut() || 
                                    mMinZoomScale < mMaxZoomScale)) {
                        mZoomButtonsController.setVisible(true);
                    }
                }

                // do pan
                int newScrollX = pinLocX(mScrollX + deltaX);
                deltaX = newScrollX - mScrollX;
                int newScrollY = pinLocY(mScrollY + deltaY);
                deltaY = newScrollY - mScrollY;
                boolean done = false;
                if (deltaX == 0 && deltaY == 0) {
                    done = true;
                } else {
                    if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_Y) {
                        int ax = Math.abs(deltaX);
                        int ay = Math.abs(deltaY);
                        if (mSnapScrollMode == SNAP_X) {
                            // radical change means getting out of snap mode
                            if (ay > MAX_SLOPE_FOR_DIAG * ax
                                    && ay > MIN_BREAK_SNAP_CROSS_DISTANCE) {
                                mSnapScrollMode = SNAP_NONE;
                            }
                            // reverse direction means lock in the snap mode
                            if ((ax > MAX_SLOPE_FOR_DIAG * ay) &&
                                    ((mSnapPositive && 
                                    deltaX < -mMinLockSnapReverseDistance)
                                    || (!mSnapPositive &&
                                    deltaX > mMinLockSnapReverseDistance))) {
                                mSnapScrollMode = SNAP_X_LOCK;
                            }
                        } else {
                            // radical change means getting out of snap mode
                            if ((ax > MAX_SLOPE_FOR_DIAG * ay)
                                    && ax > MIN_BREAK_SNAP_CROSS_DISTANCE) {
                                mSnapScrollMode = SNAP_NONE;
                            }
                            // reverse direction means lock in the snap mode
                            if ((ay > MAX_SLOPE_FOR_DIAG * ax) &&
                                    ((mSnapPositive && 
                                    deltaY < -mMinLockSnapReverseDistance)
                                    || (!mSnapPositive && 
                                    deltaY > mMinLockSnapReverseDistance))) {
                                mSnapScrollMode = SNAP_Y_LOCK;
                            }
                        }
                    }

                    if (mSnapScrollMode == SNAP_X
                            || mSnapScrollMode == SNAP_X_LOCK) {
                        scrollBy(deltaX, 0);
                        mLastTouchX = x;
                    } else if (mSnapScrollMode == SNAP_Y
                            || mSnapScrollMode == SNAP_Y_LOCK) {
                        scrollBy(0, deltaY);
                        mLastTouchY = y;
                    } else {
                        scrollBy(deltaX, deltaY);
                        mLastTouchX = x;
                        mLastTouchY = y;
                    }
                    mLastTouchTime = eventTime;
                    mUserScroll = true;
                }

                if (!getSettings().getBuiltInZoomControls()) {
                    boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
                    boolean showMagnify = canZoomScrollOut();
                    if (mZoomControls != null && (showPlusMinus || showMagnify)) {
                        if (mZoomControls.getVisibility() == View.VISIBLE) {
                            mPrivateHandler.removeCallbacks(mZoomControlRunnable);
                        } else {
                            mZoomControls.show(showPlusMinus, showMagnify);
                        }
                        mPrivateHandler.postDelayed(mZoomControlRunnable,
                                ZOOM_CONTROLS_TIMEOUT);
                    }
                }

                if (done) {
                    // return false to indicate that we can't pan out of the
                    // view space
                    return false;
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                mLastTouchUpTime = eventTime;
                switch (mTouchMode) {
                    case TOUCH_INIT_MODE: // tap
                    case TOUCH_SHORTPRESS_START_MODE:
                    case TOUCH_SHORTPRESS_MODE:
                        mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
                        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
                        mTouchMode = TOUCH_DONE_MODE;
                        doShortPress();
                        break;
                    case TOUCH_SELECT_MODE:
                        commitCopy();
                        mTouchSelection = false;
                        break;
                    case SCROLL_ZOOM_ANIMATION_IN:
                    case SCROLL_ZOOM_ANIMATION_OUT:
                        // no action during scroll animation
                        break;
                    case SCROLL_ZOOM_OUT:
                        if (LOGV_ENABLED) {
                            Log.v(LOGTAG, "ACTION_UP SCROLL_ZOOM_OUT"
                                    + " eventTime - mLastTouchTime="
                                    + (eventTime - mLastTouchTime));
                        }
                        // for now, always zoom back when the drag completes
                        if (true || eventTime - mLastTouchTime < TAP_TIMEOUT) {
                            // but if we tap, zoom in where we tap
                            if (eventTime - mLastTouchTime < TAP_TIMEOUT) {
                                zoomScrollTap(x, y);
                            }
                            // start zooming in back to the original view
                            setZoomScrollIn();
                            mTouchMode = SCROLL_ZOOM_ANIMATION_IN;
                            invalidate();
                        }
                        break;
                    case TOUCH_DRAG_MODE:
                        // if the user waits a while w/o moving before the
                        // up, we don't want to do a fling
                        if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
                            mVelocityTracker.addMovement(ev);
                            doFling();
                            break;
                        }
                        WebViewCore.resumeUpdate(mWebViewCore);
                        break;
                    case TOUCH_DRAG_START_MODE:
                    case TOUCH_DONE_MODE:
                        // do nothing
                        break;
                }
                // we also use mVelocityTracker == null to tell us that we are
                // not "moving around", so we can take the slower/prettier
                // mode in the drawing code
                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                break;
            }
            case MotionEvent.ACTION_CANCEL: {
                // we also use mVelocityTracker == null to tell us that we are
                // not "moving around", so we can take the slower/prettier
                // mode in the drawing code
                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                if (mTouchMode == SCROLL_ZOOM_OUT ||
                        mTouchMode == SCROLL_ZOOM_ANIMATION_IN) {
                    scrollTo(mZoomScrollX, mZoomScrollY);
                } else if (mTouchMode == TOUCH_DRAG_MODE) {
                    WebViewCore.resumeUpdate(mWebViewCore);
                }
                mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
                mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
                mTouchMode = TOUCH_DONE_MODE;
                int contentX = viewToContent((int) mLastTouchX + mScrollX);
                int contentY = viewToContent((int) mLastTouchY + mScrollY);
                if (inEditingMode()) {
                    mTextEntry.updateCachedTextfield();
                }
                nativeClearFocus(contentX, contentY);
                break;
            }
        }
        return true;
    
public booleanonTrackballEvent(android.view.MotionEvent ev)

        long time = ev.getEventTime();
        if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
            if (ev.getY() > 0) pageDown(true);
            if (ev.getY() < 0) pageUp(true);
            return true;
        }
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            mPrivateHandler.removeMessages(SWITCH_TO_ENTER);
            mTrackballDown = true;
            if (mNativeClass != 0) {
                nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
            }
            if (time - mLastFocusTime <= TRACKBALL_TIMEOUT
                    && !mLastFocusBounds.equals(nativeGetFocusRingBounds())) {
                nativeSelectBestAt(mLastFocusBounds);
            }
            if (LOGV_ENABLED) {
                Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
                        + " time=" + time 
                        + " mLastFocusTime=" + mLastFocusTime);
            }
            if (isInTouchMode()) requestFocusFromTouch();
            return false; // let common code in onKeyDown at it
        } 
        if (ev.getAction() == MotionEvent.ACTION_UP) {
            // LONG_PRESS_ENTER is set in common onKeyDown
            mPrivateHandler.removeMessages(LONG_PRESS_ENTER);
            mTrackballDown = false;
            mTrackballUpTime = time;
            if (mShiftIsPressed) {
                if (mExtendSelection) {
                    commitCopy();
                } else {
                    mExtendSelection = true;
                }
            }
            if (LOGV_ENABLED) {
                Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
                        + " time=" + time 
                );
            }
            return false; // let common code in onKeyUp at it
        }
        if (mMapTrackballToArrowKeys && mShiftIsPressed == false) {
            if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent gmail quit");
            return false;
        }
        // no move if we're still waiting on SWITCH_TO_ENTER timeout
        if (mTouchMode == TOUCH_DOUBLECLICK_MODE) {
            if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent 2 click quit");
            return true;
        }
        if (mTrackballDown) {
            if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent down quit");
            return true; // discard move if trackball is down
        }
        if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
            if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
            return true;
        }
        // TODO: alternatively we can do panning as touch does
        switchOutDrawHistory();
        if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
            if (LOGV_ENABLED) {
                Log.v(LOGTAG, "onTrackballEvent time=" 
                        + time + " last=" + mTrackballLastTime);
            }
            mTrackballFirstTime = time;
            mTrackballXMove = mTrackballYMove = 0;
        }
        mTrackballLastTime = time;
        if (LOGV_ENABLED) {
            Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
        }
        mTrackballRemainsX += ev.getX();
        mTrackballRemainsY += ev.getY();
        doTrackball(time);
        return true;
    
public voidonWindowFocusChanged(boolean hasWindowFocus)

        if (hasWindowFocus) {
            if (hasFocus()) {
                // If our window regained focus, and we have focus, then begin
                // drawing the focus ring, and restore the TextView if
                // necessary.
                mDrawFocusRing = true;
                if (mNeedsUpdateTextEntry) {
                    updateTextEntry();
                }
                if (mNativeClass != 0) {
                    nativeRecordButtons(true, false, true);
                }
            } else {
                // If our window gained focus, but we do not have it, do not
                // draw the focus ring.
                mDrawFocusRing = false;
                // We do not call nativeRecordButtons here because we assume
                // that when we lost focus, or window focus, it got called with
                // false for the first parameter
            }
        } else {
            if (getSettings().getBuiltInZoomControls() && !mZoomButtonsController.isVisible()) {
                /*
                 * The zoom controls come in their own window, so our window
                 * loses focus. Our policy is to not draw the focus ring if
                 * our window is not focused, but this is an exception since
                 * the user can still navigate the web page with the zoom
                 * controls showing.
                 */
                // If our window has lost focus, stop drawing the focus ring
                mDrawFocusRing = false;
            }
            mGotKeyDown = false;
            mShiftIsPressed = false;
            if (mNativeClass != 0) {
                nativeRecordButtons(false, false, true);
            }
        }
        invalidate();
        super.onWindowFocusChanged(hasWindowFocus);
    
public booleanoverlayHorizontalScrollbar()
Return whether horizontal scrollbar has overlay style

return
TRUE if horizontal scrollbar has overlay style.

        return mOverlayHorizontalScrollbar;
    
public booleanoverlayVerticalScrollbar()
Return whether vertical scrollbar has overlay style

return
TRUE if vertical scrollbar has overlay style.

        return mOverlayVerticalScrollbar;
    
private voidoverrideLoading(java.lang.String url)

        mCallbackProxy.uiOverrideUrlLoading(url);
    
public booleanpageDown(boolean bottom)
Scroll the contents of the view down by half the page size

param
bottom true to jump to bottom of page
return
true if the page was scrolled

        if (mNativeClass == 0) {
            return false;
        }
        nativeClearFocus(-1, -1);
        if (bottom) {
            return pinScrollTo(mScrollX, mContentHeight, true, 0);
        }
        // Page down.
        int h = getHeight();
        int y;
        if (h > 2 * PAGE_SCROLL_OVERLAP) {
            y = h - PAGE_SCROLL_OVERLAP;
        } else {
            y = h / 2;
        }
        mUserScroll = true;
        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0) 
                : extendScroll(y);
    
public booleanpageUp(boolean top)
Scroll the contents of the view up by half the view size

param
top true to jump to the top of the page
return
true if the page was scrolled

        if (mNativeClass == 0) {
            return false;
        }
        nativeClearFocus(-1, -1);
        if (top) {
            // go to the top of the document
            return pinScrollTo(mScrollX, 0, true, 0);
        }
        // Page up
        int h = getHeight();
        int y;
        if (h > 2 * PAGE_SCROLL_OVERLAP) {
            y = -h + PAGE_SCROLL_OVERLAP;
        } else {
            y = -h / 2;
        }
        mUserScroll = true;
        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0) 
                : extendScroll(y);
    
voidpassToJavaScript(java.lang.String currentText, android.view.KeyEvent event)

        HashMap arg = new HashMap();
        arg.put("focusData", new WebViewCore.FocusData(mFocusData));
        arg.put("event", event);
        arg.put("currentText", currentText);
        // Increase our text generation number, and pass it to webcore thread
        mTextGeneration++;
        mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
        // WebKit's document state is not saved until about to leave the page.
        // To make sure the host application, like Browser, has the up to date 
        // document state when it goes to background, we force to save the 
        // document state.
        mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
        mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
                new WebViewCore.FocusData(mFocusData), 1000);
    
public voidpauseTimers()
Pause all layout, parsing, and javascript timers. This can be useful if the WebView is not visible or the application has been paused.

        mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
    
public booleanperformLongClick()

        if (inEditingMode()) {
            return mTextEntry.performLongClick();
        } else {
            return super.performLongClick();
        }
    
private static intpinLoc(int x, int viewMax, int docMax)

//        Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
        if (docMax < viewMax) {   // the doc has room on the sides for "blank"
            // pin the short document to the top/left of the screen
            x = 0;
//            Log.d(LOGTAG, "--- center " + x);
        } else if (x < 0) {
            x = 0;
//            Log.d(LOGTAG, "--- zero");
        } else if (x + viewMax > docMax) {
            x = docMax - viewMax;
//            Log.d(LOGTAG, "--- pin " + x);
        }
        return x;
    
private intpinLocX(int x)

        return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
    
private intpinLocY(int y)

        return pinLoc(y, getViewHeight(), computeVerticalScrollRange());
    
private booleanpinScrollBy(int dx, int dy, boolean animate, int animationDuration)

        return pinScrollTo(mScrollX + dx, mScrollY + dy, animate, animationDuration);
    
private booleanpinScrollTo(int x, int y, boolean animate, int animationDuration)

        x = pinLocX(x);
        y = pinLocY(y);
        int dx = x - mScrollX;
        int dy = y - mScrollY;

        if ((dx | dy) == 0) {
            return false;
        }

        if (true && animate) {
            //        Log.d(LOGTAG, "startScroll: " + dx + " " + dy);

            mScroller.startScroll(mScrollX, mScrollY, dx, dy,
                    animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
            invalidate();
        } else {
            mScroller.abortAnimation(); // just in case
            scrollTo(x, y);
        }
        return true;
    
private voidrecordNewContentSize(int w, int h, boolean updateLayout)


        // premature data from webkit, ignore
        if ((w | h) == 0) {
            return;
        }
        
        // don't abort a scroll animation if we didn't change anything
        if (mContentWidth != w || mContentHeight != h) {
            // record new dimensions
            mContentWidth = w;
            mContentHeight = h;
            // If history Picture is drawn, don't update scroll. They will be
            // updated when we get out of that mode.
            if (!mDrawHistory) {
                // repin our scroll, taking into account the new content size
                int oldX = mScrollX;
                int oldY = mScrollY;
                mScrollX = pinLocX(mScrollX);
                mScrollY = pinLocY(mScrollY);
                // android.util.Log.d("skia", "recordNewContentSize -
                // abortAnimation");
                mScroller.abortAnimation(); // just in case
                if (oldX != mScrollX || oldY != mScrollY) {
                    sendOurVisibleRect();
                }
            }
        }
        contentSizeChanged(updateLayout);
    
public voidrefreshPlugins(boolean reloadOpenPages)
Signal the WebCore thread to refresh its list of plugins. Use this if the directory contents of one of the plugin directories has been modified and needs its changes reflecting. May cause plugin load and/or unload.

param
reloadOpenPages Set to true to reload all open pages.

        if (mWebViewCore != null) {
            mWebViewCore.sendMessage(EventHub.REFRESH_PLUGINS, reloadOpenPages);
        }
    
public voidreload()
Reload the current url.

        switchOutDrawHistory();
        mWebViewCore.sendMessage(EventHub.RELOAD);
    
voidreplaceTextfieldText(int oldStart, int oldEnd, java.lang.String replace, int newStart, int newEnd)

        HashMap arg = new HashMap();
        arg.put("focusData", new WebViewCore.FocusData(mFocusData));
        arg.put("replace", replace);
        arg.put("start", new Integer(newStart));
        arg.put("end", new Integer(newEnd));
        mTextGeneration++;
        mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
    
public booleanrequestChildRectangleOnScreen(android.view.View child, android.graphics.Rect rect, boolean immediate)

        rect.offset(child.getLeft() - child.getScrollX(),
                child.getTop() - child.getScrollY());

        int height = getHeight() - getHorizontalScrollbarHeight();
        int screenTop = mScrollY;
        int screenBottom = screenTop + height;

        int scrollYDelta = 0;

        if (rect.bottom > screenBottom && rect.top > screenTop) {
            if (rect.height() > height) {
                scrollYDelta += (rect.top - screenTop);
            } else {
                scrollYDelta += (rect.bottom - screenBottom);
            }
        } else if (rect.top < screenTop) {
            scrollYDelta -= (screenTop - rect.top);
        }

        int width = getWidth() - getVerticalScrollbarWidth();
        int screenLeft = mScrollX;
        int screenRight = screenLeft + width;

        int scrollXDelta = 0;

        if (rect.right > screenRight && rect.left > screenLeft) {
            if (rect.width() > width) {
                scrollXDelta += (rect.left - screenLeft);
            } else {
                scrollXDelta += (rect.right - screenRight);
            }
        } else if (rect.left < screenLeft) {
            scrollXDelta -= (screenLeft - rect.left);
        }

        if ((scrollYDelta | scrollXDelta) != 0) {
            return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
        }

        return false;
    
public booleanrequestFocus(int direction, android.graphics.Rect previouslyFocusedRect)

        boolean result = false;
        if (inEditingMode()) {
            result = mTextEntry.requestFocus(direction, previouslyFocusedRect);
        } else {
            result = super.requestFocus(direction, previouslyFocusedRect);
            if (mWebViewCore.getSettings().getNeedInitialFocus()) {
                // For cases such as GMail, where we gain focus from a direction,
                // we want to move to the first available link.
                // FIXME: If there are no visible links, we may not want to
                int fakeKeyDirection = 0;
                switch(direction) {
                    case View.FOCUS_UP:
                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
                        break;
                    case View.FOCUS_DOWN:
                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
                        break;
                    case View.FOCUS_LEFT:
                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
                        break;
                    case View.FOCUS_RIGHT:
                        fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
                        break;
                    default:
                        return result;
                }
                if (mNativeClass != 0 && !nativeUpdateFocusNode()) {
                    navHandledKey(fakeKeyDirection, 1, true, 0);
                }
            }
        }
        return result;
    
public voidrequestFocusNodeHref(android.os.Message hrefMsg)
Request the href of an anchor element due to getFocusNodePath returning "href." If hrefMsg is null, this method returns immediately and does not dispatch hrefMsg to its target.

param
hrefMsg This message will be dispatched with the result of the request as the data member with "url" as key. The result can be null.

        if (hrefMsg == null || mNativeClass == 0) {
            return;
        }
        if (nativeUpdateFocusNode()) {
            FocusNode node = mFocusNode;
            if (node.mIsAnchor) {
                // NOTE: We may already have the url of the anchor stored in
                // node.mText but it may be out of date or the caller may want
                // to know about javascript urls.
                mWebViewCore.sendMessage(EventHub.REQUEST_FOCUS_HREF,
                        node.mFramePointer, node.mNodePointer, hrefMsg);
            }
        }
    
public voidrequestImageRef(android.os.Message msg)
Request the url of the image last touched by the user. msg will be sent to its target with a String representing the url as its object.

param
msg This message will be dispatched with the result of the request as the data member with "url" as key. The result can be null.

        int contentX = viewToContent((int) mLastTouchX + mScrollX);
        int contentY = viewToContent((int) mLastTouchY + mScrollY);
        String ref = nativeImageURI(contentX, contentY);
        Bundle data = msg.getData();
        data.putString("url", ref);
        msg.setData(data);
        msg.sendToTarget();
    
voidrequestListBox(java.lang.String[] array, boolean[] enabledArray, int[] selectedArray)

        mPrivateHandler.post(
                new InvokeListBox(array, enabledArray, selectedArray));
    
voidrequestListBox(java.lang.String[] array, boolean[] enabledArray, int selection)

        mPrivateHandler.post(
                new InvokeListBox(array, enabledArray, selection));
    
voidresetTrackballTime()

        mTrackballLastTime = 0;
    
public booleanrestorePicture(android.os.Bundle b, java.io.File src)
Restore the display data that was save in {@link #savePicture}. Used in conjunction with {@link #restoreState}.

param
b A Bundle containing the saved display data.
param
src The file where the picture data was stored.
return
True if the picture was successfully restored.

        if (src == null || b == null) {
            return false;
        }
        if (src.exists()) {
            Picture p = null;
            try {
                final FileInputStream in = new FileInputStream(src);
                p = Picture.createFromStream(in);
                in.close();
            } catch (FileNotFoundException e){
                e.printStackTrace();
            } catch (RuntimeException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (p != null) {
                int sx = b.getInt("scrollX", 0);
                int sy = b.getInt("scrollY", 0);
                float scale = b.getFloat("scale", 1.0f);
                mDrawHistory = true;
                mHistoryPicture = p;
                mScrollX = sx;
                mScrollY = sy;
                mHistoryWidth = Math.round(p.getWidth() * scale);
                mHistoryHeight = Math.round(p.getHeight() * scale);
                // as getWidth() / getHeight() of the view are not
                // available yet, set up mActualScale, so that when
                // onSizeChanged() is called, the rest will be set
                // correctly
                mActualScale = scale;
                invalidate();
                return true;
            }
        }
        return false;
    
public WebBackForwardListrestoreState(android.os.Bundle inState)
Restore the state of this WebView from the given map used in {@link android.app.Activity#onRestoreInstanceState}. This method should be called to restore the state of the WebView before using the object. If it is called after the WebView has had a chance to build state (load pages, create a back/forward list, etc.) there may be undesirable side-effects. Please note that this method no longer restores the display data for this WebView. See {@link #savePicture} and {@link #restorePicture} for saving and restoring the display data.

param
inState The incoming Bundle of state.
return
The restored back/forward list or null if restoreState failed.
see
#savePicture
see
#restorePicture

        WebBackForwardList returnList = null;
        if (inState == null) {
            return returnList;
        }
        if (inState.containsKey("index") && inState.containsKey("history")) {
            mCertificate = SslCertificate.restoreState(
                inState.getBundle("certificate"));

            final WebBackForwardList list = mCallbackProxy.getBackForwardList();
            final int index = inState.getInt("index");
            // We can't use a clone of the list because we need to modify the
            // shared copy, so synchronize instead to prevent concurrent
            // modifications.
            synchronized (list) {
                final List<byte[]> history =
                        (List<byte[]>) inState.getSerializable("history");
                final int size = history.size();
                // Check the index bounds so we don't crash in native code while
                // restoring the history index.
                if (index < 0 || index >= size) {
                    return null;
                }
                for (int i = 0; i < size; i++) {
                    byte[] data = history.remove(0);
                    if (data == null) {
                        // If we somehow have null data, we cannot reconstruct
                        // the item and thus our history list cannot be rebuilt.
                        return null;
                    }
                    WebHistoryItem item = new WebHistoryItem(data);
                    list.addHistoryItem(item);
                }
                // Grab the most recent copy to return to the caller.
                returnList = copyBackForwardList();
                // Update the copy to have the correct index.
                returnList.setCurrentIndex(index);
            }
            // Remove all pending messages because we are restoring previous
            // state.
            mWebViewCore.removeMessages();
            // Send a restore state message.
            mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
        }
        return returnList;
    
public voidresumeTimers()
Resume all layout, parsing, and javascript timers. This will resume dispatching all timers.

        mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
    
public voidsavePassword(java.lang.String host, java.lang.String username, java.lang.String password)
Save the username and password for a particular host in the WebView's internal database.

param
host The host that required the credentials.
param
username The username for the given host.
param
password The password for the given host.

        mDatabase.setUsernamePassword(host, username, password);
    
public booleansavePicture(android.os.Bundle b, java.io.File dest)
Save the current display data to the Bundle given. Used in conjunction with {@link #saveState}.

param
b A Bundle to store the display data.
param
dest The file to store the serialized picture data. Will be overwritten with this WebView's picture data.
return
True if the picture was successfully saved.

        if (dest == null || b == null) {
            return false;
        }
        final Picture p = capturePicture();
        try {
            final FileOutputStream out = new FileOutputStream(dest);
            p.writeToStream(out);
            out.close();
        } catch (FileNotFoundException e){
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (RuntimeException e) {
            e.printStackTrace();
        }
        if (dest.length() > 0) {
            b.putInt("scrollX", mScrollX);
            b.putInt("scrollY", mScrollY);
            b.putFloat("scale", mActualScale);
            return true;
        }
        return false;
    
public WebBackForwardListsaveState(android.os.Bundle outState)
Save the state of this WebView used in {@link android.app.Activity#onSaveInstanceState}. Please note that this method no longer stores the display data for this WebView. The previous behavior could potentially leak files if {@link #restoreState} was never called. See {@link #savePicture} and {@link #restorePicture} for saving and restoring the display data.

param
outState The Bundle to store the WebView state.
return
The same copy of the back/forward list used to save the state. If saveState fails, the returned list will be null.
see
#savePicture
see
#restorePicture

        if (outState == null) {
            return null;
        }
        // We grab a copy of the back/forward list because a client of WebView
        // may have invalidated the history list by calling clearHistory.
        WebBackForwardList list = copyBackForwardList();
        final int currentIndex = list.getCurrentIndex();
        final int size = list.getSize();
        // We should fail saving the state if the list is empty or the index is
        // not in a valid range.
        if (currentIndex < 0 || currentIndex >= size || size == 0) {
            return null;
        }
        outState.putInt("index", currentIndex);
        // FIXME: This should just be a byte[][] instead of ArrayList but
        // Parcel.java does not have the code to handle multi-dimensional
        // arrays.
        ArrayList<byte[]> history = new ArrayList<byte[]>(size);
        for (int i = 0; i < size; i++) {
            WebHistoryItem item = list.getItemAtIndex(i);
            byte[] data = item.getFlattenedData();
            if (data == null) {
                // It would be very odd to not have any data for a given history
                // item. And we will fail to rebuild the history list without
                // flattened data.
                return null;
            }
            history.add(data);
        }
        outState.putSerializable("history", history);
        if (mCertificate != null) {
            outState.putBundle("certificate",
                               SslCertificate.saveState(mCertificate));
        }
        return list;
    
private intscaleTrackballX(float xRate, int width)

        int xMove = (int) (xRate / TRACKBALL_SCALE * width);
        int nextXMove = xMove;
        if (xMove > 0) {
            if (xMove > mTrackballXMove) {
                xMove -= mTrackballXMove;
            }
        } else if (xMove < mTrackballXMove) {
            xMove -= mTrackballXMove;
        }
        mTrackballXMove = nextXMove;
        return xMove;
    
private intscaleTrackballY(float yRate, int height)

        int yMove = (int) (yRate / TRACKBALL_SCALE * height);
        int nextYMove = yMove;
        if (yMove > 0) {
            if (yMove > mTrackballYMove) {
                yMove -= mTrackballYMove;
            }
        } else if (yMove < mTrackballYMove) {
            yMove -= mTrackballYMove;
        }
        mTrackballYMove = nextYMove;
        return yMove;
    
private voidscrollZoomDraw(android.graphics.Canvas canvas)

        float invScale = mZoomScrollInvLimit; 
        int elapsed = 0;
        if (mTouchMode != SCROLL_ZOOM_OUT) {
            elapsed = (int) Math.min(System.currentTimeMillis() 
                - mZoomScrollStart, SCROLL_ZOOM_DURATION);
            float transitionScale = (mZoomScrollInvLimit - mInvActualScale) 
                    * elapsed / SCROLL_ZOOM_DURATION;
            if (mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
                invScale = mInvActualScale + transitionScale;
            } else { /* if (mTouchMode == SCROLL_ZOOM_ANIMATION_IN) */
                invScale = mZoomScrollInvLimit - transitionScale;
            }
        }
        float scale = scrollZoomGridScale(invScale);
        invScale = 1.0f / scale;
        int width = getViewWidth();
        int height = getViewHeight();
        float halfScale = scrollZoomMagScale(invScale);
        Rect scrollFrame = scrollZoomFrame(width, height, halfScale);
        if (elapsed == SCROLL_ZOOM_DURATION) {
            if (mTouchMode == SCROLL_ZOOM_ANIMATION_IN) {
                setHorizontalScrollBarEnabled(true);
                setVerticalScrollBarEnabled(true);
                updateTextEntry();
                scrollTo((int) (scrollFrame.centerX() * mActualScale) 
                        - (width >> 1), (int) (scrollFrame.centerY() 
                        * mActualScale) - (height >> 1));
                mTouchMode = TOUCH_DONE_MODE;
            } else {
                mTouchMode = SCROLL_ZOOM_OUT;
            }
        }
        float newX = scrollZoomX(scale);
        float newY = scrollZoomY(scale);
        if (LOGV_ENABLED) {
            Log.v(LOGTAG, "scrollZoomDraw scale=" + scale + " + (" + newX
                    + ", " + newY + ") mZoomScroll=(" + mZoomScrollX + ", "
                    + mZoomScrollY + ")" + " invScale=" + invScale + " scale=" 
                    + scale);
        }
        canvas.translate(newX, newY);
        canvas.scale(scale, scale);
        boolean animating = mTouchMode != SCROLL_ZOOM_OUT;
        if (mDrawHistory) {
            int sc = canvas.save(Canvas.CLIP_SAVE_FLAG);
            Rect clip = new Rect(0, 0, mHistoryPicture.getWidth(),
                    mHistoryPicture.getHeight());
            canvas.clipRect(clip, Region.Op.DIFFERENCE);
            canvas.drawColor(mBackgroundColor);
            canvas.restoreToCount(sc);
            canvas.drawPicture(mHistoryPicture);
        } else {
            mWebViewCore.drawContentPicture(canvas, mBackgroundColor,
                    animating, true);
        }
        if (mTouchMode == TOUCH_DONE_MODE) {
            return;
        }
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(30.0f);
        paint.setARGB(0x50, 0, 0, 0);
        int maxX = mContentWidth - width;
        int maxY = mContentHeight - height;
        if (true) { // experiment: draw hint to place finger off magnify area
            drawMagnifyFrame(canvas, scrollFrame, paint);
        } else {
            canvas.drawRect(scrollFrame, paint);
        }
        int sc = canvas.save();
        canvas.clipRect(scrollFrame);
        float halfX = (float) mZoomScrollX / maxX;
        if (mContentWidth * mZoomScrollLimit < width) {
            halfX = zoomFrameScaleX(width, 0.5f, halfX);
        }
        float halfY = (float) mZoomScrollY / maxY;
        if (mContentHeight * mZoomScrollLimit < height) {
            halfY = zoomFrameScaleY(height, 0.5f, halfY);
        }
        canvas.scale(halfScale, halfScale, mZoomScrollX + width * halfX
                , mZoomScrollY + height * halfY);
        if (LOGV_ENABLED) {
            Log.v(LOGTAG, "scrollZoomDraw halfScale=" + halfScale + " w/h=(" 
                    + width + ", " + height + ") half=(" + halfX + ", "
                    + halfY + ")");
        }
        if (mDrawHistory) {
            canvas.drawPicture(mHistoryPicture);
        } else {
            mWebViewCore.drawContentPicture(canvas, mBackgroundColor,
                    animating, false);
        }
        canvas.restoreToCount(sc);
        if (mTouchMode != SCROLL_ZOOM_OUT) {
            invalidate();
        }
    
private android.graphics.RectscrollZoomFrame(int width, int height, float halfScale)

        Rect scrollFrame = new Rect();
        scrollFrame.set(mZoomScrollX, mZoomScrollY, 
                mZoomScrollX + width, mZoomScrollY + height);
        if (mContentWidth * mZoomScrollLimit < width) {
            float scale = zoomFrameScaleX(width, halfScale, 1.0f);
            float offsetX = (width * scale - width) * 0.5f;
            scrollFrame.left -= offsetX;
            scrollFrame.right += offsetX;
        }
        if (mContentHeight * mZoomScrollLimit < height) {
            float scale = zoomFrameScaleY(height, halfScale, 1.0f);
            float offsetY = (height * scale - height) * 0.5f;
            scrollFrame.top -= offsetY;
            scrollFrame.bottom += offsetY;
        }
        return scrollFrame;
    
private floatscrollZoomGridScale(float invScale)

        float griddedInvScale = (int) (invScale * SCROLL_ZOOM_GRID) 
            / (float) SCROLL_ZOOM_GRID;
        return 1.0f / griddedInvScale;
    
private floatscrollZoomMagScale(float invScale)

        return (invScale * 2 + mInvActualScale) / 3;
    
private floatscrollZoomX(float scale)

        int width = getViewWidth();
        float maxScrollZoomX = mContentWidth * scale - width;
        int maxX = mContentWidth - width;
        return -(maxScrollZoomX > 0 ? mZoomScrollX * maxScrollZoomX / maxX
                : maxScrollZoomX / 2);
    
private floatscrollZoomY(float scale)

        int height = getViewHeight();
        float maxScrollZoomY = mContentHeight * scale - height;
        int maxY = mContentHeight - height;
        return -(maxScrollZoomY > 0 ? mZoomScrollY * maxScrollZoomY / maxY
                : maxScrollZoomY / 2);
    
private voidsendFinalFocus(int frame, int node, int x, int y)

        WebViewCore.FocusData focusData = new WebViewCore.FocusData();
        focusData.mFrame = frame;
        focusData.mNode = node;
        focusData.mX = x;
        focusData.mY = y;
        mWebViewCore.sendMessage(EventHub.SET_FINAL_FOCUS, 
                EventHub.NO_FOCUS_CHANGE_BLOCK, 0, focusData);
    
private voidsendKitFocus()

        WebViewCore.FocusData focusData = new WebViewCore.FocusData(mFocusData);
        mWebViewCore.sendMessage(EventHub.SET_KIT_FOCUS, focusData);
    
private voidsendMotionUp(int touchGeneration, int buildGeneration, int frame, int node, int x, int y, int size, boolean isClick, boolean retry)

        WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
        touchUpData.mMoveGeneration = touchGeneration;
        touchUpData.mBuildGeneration = buildGeneration;
        touchUpData.mSize = size;
        touchUpData.mIsClick = isClick;
        touchUpData.mRetry = retry;
        mFocusData.mFrame = touchUpData.mFrame = frame;
        mFocusData.mNode = touchUpData.mNode = node;
        mFocusData.mX = touchUpData.mX = x;
        mFocusData.mY = touchUpData.mY = y;
        mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
    
private android.graphics.RectsendOurVisibleRect()

        Rect rect = new Rect();
        calcOurContentVisibleRect(rect);
        if (mFindIsUp) {
            rect.bottom -= viewToContent(FIND_HEIGHT);
        }
        // Rect.equals() checks for null input.
        if (!rect.equals(mLastVisibleRectSent)) {
            mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
                                     rect.left, rect.top);
            mLastVisibleRectSent = rect;
        }
        Rect globalRect = new Rect();
        if (getGlobalVisibleRect(globalRect)
                && !globalRect.equals(mLastGlobalRect)) {
            // TODO: the global offset is only used by windowRect()
            // in ChromeClientAndroid ; other clients such as touch
            // and mouse events could return view + screen relative points.
            mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, globalRect);
            mLastGlobalRect = globalRect;
        }
        return rect;
    
private booleansendViewSizeZoom()
Compute unzoomed width and height, and if they differ from the last values we sent, send them to webkit (to be used has new viewport)

return
true if new values were sent

        int newWidth = Math.round(getViewWidth() * mInvActualScale);
        int newHeight = Math.round(getViewHeight() * mInvActualScale);
        /*
         * Because the native side may have already done a layout before the
         * View system was able to measure us, we have to send a height of 0 to
         * remove excess whitespace when we grow our width. This will trigger a
         * layout and a change in content size. This content size change will
         * mean that contentSizeChanged will either call this method directly or
         * indirectly from onSizeChanged.
         */
        if (newWidth > mLastWidthSent && mWrapContent) {
            newHeight = 0;
        }
        // Avoid sending another message if the dimensions have not changed.
        if (newWidth != mLastWidthSent || newHeight != mLastHeightSent) {
            mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED,
                    newWidth, newHeight, new Float(mActualScale));
            mLastWidthSent = newWidth;
            mLastHeightSent = newHeight;
            return true;
        }
        return false;
    
public voidsetBackgroundColor(int color)
Set the background color. It's white by default. Pass zero to make the view transparent.

param
color the ARGB color described by Color.java

        mBackgroundColor = color;
        mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
    
public voidsetCertificate(android.net.http.SslCertificate certificate)
Sets the SSL certificate for the main top-level page.

        // here, the certificate can be null (if the site is not secure)
        mCertificate = certificate;
    
private voidsetContentScrollBy(int cx, int cy, boolean animate)

        if (mDrawHistory) {
            // disallow WebView to change the scroll position as History Picture
            // is used in the view system.
            // TODO: as we switchOutDrawHistory when trackball or navigation
            // keys are hit, this should be safe. Right?
            return;
        }
        cx = contentToView(cx);
        cy = contentToView(cy);
        if (mHeightCanMeasure) {
            // move our visible rect according to scroll request
            if (cy != 0) {
                Rect tempRect = new Rect();
                calcOurVisibleRect(tempRect);
                tempRect.offset(cx, cy);
                requestRectangleOnScreen(tempRect);
            }
            // FIXME: We scroll horizontally no matter what because currently
            // ScrollView and ListView will not scroll horizontally.
            // FIXME: Why do we only scroll horizontally if there is no
            // vertical scroll?
//                Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
            if (cy == 0 && cx != 0) {
                pinScrollBy(cx, 0, animate, 0);
            }
        } else {
            pinScrollBy(cx, cy, animate, 0);
        }
    
private booleansetContentScrollTo(int cx, int cy)

        if (mDrawHistory) {
            // disallow WebView to change the scroll position as History Picture
            // is used in the view system.
            // One known case where this is called is that WebCore tries to
            // restore the scroll position. As history Picture already uses the
            // saved scroll position, it is ok to skip this.
            return false;
        }
        int vx = contentToView(cx);
        int vy = contentToView(cy);
//        Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" +
//                      vx + " " + vy + "]");
        pinScrollTo(vx, vy, false, 0);
        if (mScrollX != vx || mScrollY != vy) {
            return true;
        } else {
            return false;
        }
    
public voidsetDownloadListener(DownloadListener listener)
Register the interface to be used when content can not be handled by the rendering engine, and should be downloaded instead. This will replace the current handler.

param
listener An implementation of DownloadListener.

        mCallbackProxy.setDownloadListener(listener);
    
private voidsetFocusData(int moveGeneration, int buildGeneration, int frame, int node, int x, int y, boolean ignoreNullFocus)

        mFocusData.mMoveGeneration = moveGeneration;
        mFocusData.mBuildGeneration = buildGeneration;
        mFocusData.mFrame = frame;
        mFocusData.mNode = node;
        mFocusData.mX = x;
        mFocusData.mY = y;
        mFocusData.mIgnoreNullFocus = ignoreNullFocus;
    
public voidsetHorizontalScrollbarOverlay(boolean overlay)
Specify whether the horizontal scrollbar has overlay style.

param
overlay TRUE if horizontal scrollbar should have overlay style.

        mOverlayHorizontalScrollbar = overlay;
    
public voidsetHttpAuthUsernamePassword(java.lang.String host, java.lang.String realm, java.lang.String username, java.lang.String password)
Set the HTTP authentication credentials for a given host and realm.

param
host The host for the credentials.
param
realm The realm for the credentials.
param
username The username for the password. If it is null, it means password can't be saved.
param
password The password

        mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
    
public voidsetInitialScale(int scaleInPercent)
Set the initial scale for the WebView. 0 means default. If {@link WebSettings#getUseWideViewPort()} is true, it zooms out all the way. Otherwise it starts with 100%. If initial scale is greater than 0, WebView starts will this value as initial scale.

param
scaleInPercent The initial scale in percent.

        mInitialScale = scaleInPercent;
    
public voidsetLayoutParams(ViewGroup.LayoutParams params)

        if (params.height == LayoutParams.WRAP_CONTENT) {
            mWrapContent = true;
        }
        super.setLayoutParams(params);
    
public voidsetMapTrackballToArrowKeys(boolean setMap)

    
        
        mMapTrackballToArrowKeys = setMap;
    
public voidsetNetworkAvailable(boolean networkUp)
Inform WebView of the network state. This is used to set the javascript property window.navigator.isOnline and generates the online/offline event as specified in HTML5, sec. 5.7.7

param
networkUp boolean indicating if network is available

        mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
                networkUp ? 1 : 0, 0);
    
private voidsetNewZoomScale(float scale, boolean force)

        if (scale < mMinZoomScale) {
            scale = mMinZoomScale;
        } else if (scale > mMaxZoomScale) {
            scale = mMaxZoomScale;
        }
        if (scale != mActualScale || force) {
            if (mDrawHistory) {
                // If history Picture is drawn, don't update scroll. They will
                // be updated when we get out of that mode.
                if (scale != mActualScale && !mPreviewZoomOnly) {
                    mCallbackProxy.onScaleChanged(mActualScale, scale);
                }
                mActualScale = scale;
                mInvActualScale = 1 / scale;
                if (!mPreviewZoomOnly) {
                    sendViewSizeZoom();
                }
            } else {
                // update our scroll so we don't appear to jump
                // i.e. keep the center of the doc in the center of the view

                int oldX = mScrollX;
                int oldY = mScrollY;
                float ratio = scale * mInvActualScale;   // old inverse
                float sx = ratio * oldX + (ratio - 1) * mZoomCenterX;
                float sy = ratio * oldY + (ratio - 1) * mZoomCenterY;

                // now update our new scale and inverse
                if (scale != mActualScale && !mPreviewZoomOnly) {
                    mCallbackProxy.onScaleChanged(mActualScale, scale);
                }
                mActualScale = scale;
                mInvActualScale = 1 / scale;

                // as we don't have animation for scaling, don't do animation 
                // for scrolling, as it causes weird intermediate state
                //        pinScrollTo(Math.round(sx), Math.round(sy));
                mScrollX = pinLocX(Math.round(sx));
                mScrollY = pinLocY(Math.round(sy));

                if (!mPreviewZoomOnly) {
                    sendViewSizeZoom();
                    sendOurVisibleRect();
                }
            }
        }
    
public voidsetPictureListener(android.webkit.WebView$PictureListener listener)
Set the Picture listener. This is an interface used to receive notifications of a new Picture.

param
listener An implementation of WebView.PictureListener.

        mPictureListener = listener;
    
public voidsetScrollBarStyle(int style)

        if (style == View.SCROLLBARS_INSIDE_INSET
                || style == View.SCROLLBARS_OUTSIDE_INSET) {
            mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
        } else {
            mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
        }
        super.setScrollBarStyle(style);
    
voidsetSelection(int start, int end)
Set the selection to (start, end) in the focused textfield. If start and end are out of order, swap them.

param
start Beginning of selection.
param
end End of selection.

        mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end,
                new WebViewCore.FocusData(mFocusData));
    
private voidsetTextEntryRect(int x, int y, int width, int height)

        x = contentToView(x);
        y = contentToView(y);
        width = contentToView(width);
        height = contentToView(height);
        mTextEntry.setRect(x, y, width, height);
    
public voidsetVerticalScrollbarOverlay(boolean overlay)
Specify whether the vertical scrollbar has overlay style.

param
overlay TRUE if vertical scrollbar should have overlay style.

        mOverlayVerticalScrollbar = overlay;
    
public voidsetWebChromeClient(WebChromeClient client)
Set the chrome handler. This is an implementation of WebChromeClient for use in handling Javascript dialogs, favicons, titles, and the progress. This will replace the current handler.

param
client An implementation of WebChromeClient.

        mCallbackProxy.setWebChromeClient(client);
    
public voidsetWebViewClient(WebViewClient client)
Set the WebViewClient that will receive various notifications and requests. This will replace the current handler.

param
client An implementation of WebViewClient.

        mCallbackProxy.setWebViewClient(client);
    
private voidsetZoomScrollIn()

        mZoomScrollStart = System.currentTimeMillis();
    
voidshortPressOnTextField()

        if (inEditingMode()) {
            View v = mTextEntry;
            int x = viewToContent((v.getLeft() + v.getRight()) >> 1);
            int y = viewToContent((v.getTop() + v.getBottom()) >> 1);
            nativeMotionUp(x, y, mNavSlop, true);
        }
    
private voidspawnContentScrollTo(int cx, int cy)

        if (mDrawHistory) {
            // disallow WebView to change the scroll position as History Picture
            // is used in the view system.
            return;
        }
        int vx = contentToView(cx);
        int vy = contentToView(cy);
        pinScrollTo(vx, vy, true, 0);
    
private voidstartZoomScrollOut()

        setHorizontalScrollBarEnabled(false);
        setVerticalScrollBarEnabled(false);
        if (getSettings().getBuiltInZoomControls()) {
            if (mZoomButtonsController.isVisible()) {
                mZoomButtonsController.setVisible(false);
            }
        } else {
            if (mZoomControlRunnable != null) {
                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
            }
            if (mZoomControls != null) {
                mZoomControls.hide();
            }
        }
        int width = getViewWidth();
        int height = getViewHeight();
        int halfW = width >> 1;
        mLastTouchX = halfW;
        int halfH = height >> 1;
        mLastTouchY = halfH;
        mScroller.abortAnimation();
        mZoomScrollStart = System.currentTimeMillis();
        Rect zoomFrame = scrollZoomFrame(width, height
                , scrollZoomMagScale(mZoomScrollInvLimit));
        mZoomScrollX = Math.max(0, (int) ((mScrollX + halfW) * mInvActualScale) 
                - (zoomFrame.width() >> 1));
        mZoomScrollY = Math.max(0, (int) ((mScrollY + halfH) * mInvActualScale) 
                - (zoomFrame.height() >> 1));
        scrollTo(0, 0); // triggers inval, starts animation
        clearTextEntry();
        if (LOGV_ENABLED) {
            Log.v(LOGTAG, "startZoomScrollOut mZoomScroll=(" 
                    + mZoomScrollX + ", " + mZoomScrollY +")");
        }
    
public voidstopLoading()
Stop the current load.

        // TODO: should we clear all the messages in the queue before sending
        // STOP_LOADING?
        switchOutDrawHistory();
        mWebViewCore.sendMessage(EventHub.STOP_LOADING);
    
voidswitchOutDrawHistory()

        if (null == mWebViewCore) return; // CallbackProxy may trigger this
        if (mDrawHistory) {
            mDrawHistory = false;
            invalidate();
            int oldScrollX = mScrollX;
            int oldScrollY = mScrollY;
            mScrollX = pinLocX(mScrollX);
            mScrollY = pinLocY(mScrollY);
            if (oldScrollX != mScrollX || oldScrollY != mScrollY) {
                mUserScroll = false;
                mWebViewCore.sendMessage(EventHub.SYNC_SCROLL, oldScrollX,
                        oldScrollY);
            }
            sendOurVisibleRect();
        }
    
voidupdateCachedTextfield(java.lang.String updatedText)
Update our cache with updatedText.

param
updatedText The new text to put in our cache.

        // Also place our generation number so that when we look at the cache
        // we recognize that it is up to date.
        nativeUpdateCachedTextfield(updatedText, mTextGeneration);
    
private voidupdateSelection()

        if (mNativeClass == 0) {
            return;
        }
        // mLastTouchX and mLastTouchY are the point in the current viewport
        int contentX = viewToContent((int) mLastTouchX + mScrollX);
        int contentY = viewToContent((int) mLastTouchY + mScrollY);
        Rect rect = new Rect(contentX - mNavSlop, contentY - mNavSlop,
                contentX + mNavSlop, contentY + mNavSlop);
        // If we were already focused on a textfield, update its cache.
        if (inEditingMode()) {
            mTextEntry.updateCachedTextfield();
        }
        nativeSelectBestAt(rect);
    
private voidupdateTextEntry()

        if (mTextEntry == null) {
            mTextEntry = new TextDialog(mContext, WebView.this);
            // Initialize our generation number.
            mTextGeneration = 0;
        }
        // If we do not have focus, do nothing until we gain focus.
        if (!hasFocus() && !mTextEntry.hasFocus()
                || (mTouchMode >= FIRST_SCROLL_ZOOM 
                && mTouchMode <= LAST_SCROLL_ZOOM)) {
            mNeedsUpdateTextEntry = true;
            return;
        }
        boolean alreadyThere = inEditingMode();
        if (0 == mNativeClass || !nativeUpdateFocusNode()) {
            if (alreadyThere) {
                mTextEntry.remove();
            }
            return;
        }
        FocusNode node = mFocusNode;
        if (!node.mIsTextField && !node.mIsTextArea) {
            if (alreadyThere) {
                mTextEntry.remove();
            }
            return;
        }
        mTextEntry.setTextSize(contentToView(node.mTextSize));
        Rect visibleRect = sendOurVisibleRect();
        // Note that sendOurVisibleRect calls viewToContent, so the coordinates
        // should be in content coordinates.
        if (!Rect.intersects(node.mBounds, visibleRect)) {
            // Node is not on screen, so do not bother.
            return;
        }
        int x = node.mBounds.left;
        int y = node.mBounds.top;
        int width = node.mBounds.width();
        int height = node.mBounds.height();
        if (alreadyThere && mTextEntry.isSameTextField(node.mNodePointer)) {
            // It is possible that we have the same textfield, but it has moved,
            // i.e. In the case of opening/closing the screen.
            // In that case, we need to set the dimensions, but not the other
            // aspects.
            // We also need to restore the selection, which gets wrecked by
            // calling setTextEntryRect.
            Spannable spannable = (Spannable) mTextEntry.getText();
            int start = Selection.getSelectionStart(spannable);
            int end = Selection.getSelectionEnd(spannable);
            setTextEntryRect(x, y, width, height);
            // If the text has been changed by webkit, update it.  However, if
            // there has been more UI text input, ignore it.  We will receive
            // another update when that text is recognized.
            if (node.mText != null && !node.mText.equals(spannable.toString())
                    && node.mRootTextGeneration == mTextGeneration) {
                mTextEntry.setTextAndKeepSelection(node.mText);
            } else {
                Selection.setSelection(spannable, start, end);
            }
        } else {
            String text = node.mText;
            setTextEntryRect(x, y, width, height);
            mTextEntry.setGravity(node.mIsRtlText ? Gravity.RIGHT : 
                    Gravity.NO_GRAVITY);
            // this needs to be called before update adapter thread starts to
            // ensure the mTextEntry has the same node pointer
            mTextEntry.setNodePointer(node.mNodePointer);
            int maxLength = -1;
            if (node.mIsTextField) {
                maxLength = node.mMaxLength;
                if (mWebViewCore.getSettings().getSaveFormData()
                        && node.mName != null) {
                    HashMap data = new HashMap();
                    data.put("text", node.mText);
                    Message update = mPrivateHandler.obtainMessage(
                            UPDATE_TEXT_ENTRY_ADAPTER, node.mNodePointer, 0,
                            data);
                    UpdateTextEntryAdapter updater = new UpdateTextEntryAdapter(
                            node.mName, getUrl(), update);
                    Thread t = new Thread(updater);
                    t.start();
                }
            }
            mTextEntry.setMaxLength(maxLength);
            AutoCompleteAdapter adapter = null;
            mTextEntry.setAdapterCustom(adapter);
            mTextEntry.setSingleLine(node.mIsTextField);
            mTextEntry.setInPassword(node.mIsPassword);
            if (null == text) {
                mTextEntry.setText("", 0, 0);
            } else {
                // Change to true to enable the old style behavior, where
                // entering a textfield/textarea always set the selection to the
                // whole field.  This was desirable for the case where the user
                // intends to scroll past the field using the trackball.
                // However, it causes a problem when replying to emails - the
                // user expects the cursor to be at the beginning of the
                // textarea.  Testing out a new behavior, where textfields set
                // selection at the end, and textareas at the beginning.
                if (false) {
                    mTextEntry.setText(text, 0, text.length());
                } else if (node.mIsTextField) {
                    int length = text.length();
                    mTextEntry.setText(text, length, length);
                } else {
                    mTextEntry.setText(text, 0, 0);
                }
            }
            mTextEntry.requestFocus();
        }
    
private voidupdateZoomButtonsEnabled()

        boolean canZoomIn = mActualScale < mMaxZoomScale;
        boolean canZoomOut = mActualScale > mMinZoomScale;
        if (!canZoomIn && !canZoomOut) {
            // Hide the zoom in and out buttons, as well as the fit to page
            // button, if the page cannot zoom
            mZoomButtonsController.getZoomControls().setVisibility(View.GONE);
            mZoomFitPageButton.setVisibility(View.GONE);
        } else {
            // Bring back the hidden zoom controls.
            mZoomButtonsController.getZoomControls()
                    .setVisibility(View.VISIBLE);
            mZoomFitPageButton.setVisibility(View.VISIBLE);
            // Set each one individually, as a page may be able to zoom in
            // or out.
            mZoomButtonsController.setZoomInEnabled(canZoomIn);
            mZoomButtonsController.setZoomOutEnabled(canZoomOut);
            mZoomFitPageButton.setEnabled(mActualScale != 1);
        }
        mZoomOverviewButton.setVisibility(canZoomScrollOut() ? View.VISIBLE:
                View.GONE);
    
private voidviewInvalidate()
Called by JNI to invalidate view

        invalidate();
    
private voidviewInvalidate(int l, int t, int r, int b)

        invalidate(contentToView(l), contentToView(t), contentToView(r),
                contentToView(b));
    
private voidviewInvalidateDelayed(long delay, int l, int t, int r, int b)

        postInvalidateDelayed(delay, contentToView(l), contentToView(t),
                contentToView(r), contentToView(b));
    
intviewToContent(int x)

        return Math.round(x * mInvActualScale);
    
private floatzoomFrameScaleX(int width, float halfScale, float noScale)

        // mContentWidth > width > mContentWidth * mZoomScrollLimit
        if (mContentWidth <= width) {
            return halfScale;
        }
        float part = (width - mContentWidth * mZoomScrollLimit)  
                / (width * (1 - mZoomScrollLimit));
        return halfScale * part + noScale * (1.0f - part);
    
private floatzoomFrameScaleY(int height, float halfScale, float noScale)

        if (mContentHeight <= height) {
            return halfScale;
        }
        float part = (height - mContentHeight * mZoomScrollLimit)  
                / (height * (1 - mZoomScrollLimit));
        return halfScale * part + noScale * (1.0f - part);
    
public booleanzoomIn()
Perform zoom in in the webview

return
TRUE if zoom in succeeds. FALSE if no zoom changes.

        // TODO: alternatively we can disallow this during draw history mode
        switchOutDrawHistory();
        return zoomWithPreview(mActualScale * 1.25f);
    
public booleanzoomOut()
Perform zoom out in the webview

return
TRUE if zoom out succeeds. FALSE if no zoom changes.

        // TODO: alternatively we can disallow this during draw history mode
        switchOutDrawHistory();
        return zoomWithPreview(mActualScale * 0.8f);
    
private voidzoomScrollOut()

        if (canZoomScrollOut() == false) {
            mTouchMode = TOUCH_DONE_MODE;
            return;
        }
        startZoomScrollOut();
        mTouchMode = SCROLL_ZOOM_ANIMATION_OUT;
        invalidate();
    
private voidzoomScrollTap(float x, float y)

        float scale = scrollZoomGridScale(mZoomScrollInvLimit);
        float left = scrollZoomX(scale);
        float top = scrollZoomY(scale);
        int width = getViewWidth();
        int height = getViewHeight();
        x -= width * scale / 2;
        y -= height * scale / 2;
        mZoomScrollX = Math.min(mContentWidth - width
                , Math.max(0, (int) ((x - left) / scale)));
        mZoomScrollY = Math.min(mContentHeight - height
                , Math.max(0, (int) ((y - top) / scale)));
        if (LOGV_ENABLED) {
            Log.v(LOGTAG, "zoomScrollTap scale=" + scale + " + (" + left
                    + ", " + top + ") mZoomScroll=(" + mZoomScrollX + ", "
                    + mZoomScrollY + ")" + " x=" + x + " y=" + y);
        }
    
private booleanzoomWithPreview(float scale)

        float oldScale = mActualScale;

        // snap to 100% if it is close
        if (scale > 0.95f && scale < 1.05f) {
            scale = 1.0f;
        }

        setNewZoomScale(scale, false);

        if (oldScale != mActualScale) {
            // use mZoomPickerScale to see zoom preview first
            mZoomStart = SystemClock.uptimeMillis();
            mInvInitialZoomScale = 1.0f / oldScale;
            mInvFinalZoomScale = 1.0f / mActualScale;
            mZoomScale = mActualScale;
            invalidate();
            return true;
        } else {
            return false;
        }