diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-02-19 10:57:31 -0800 | 
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-02-19 10:57:31 -0800 | 
| commit | 3001a035439d8134a7d70d796376d1dfbff3cdcd (patch) | |
| tree | 343ccdba15a594ff6e50c874a145232753315a30 /core/java/android/webkit | |
| parent | da996f390e17e16f2dfa60e972e7ebc4f868f37e (diff) | |
| download | frameworks_base-3001a035439d8134a7d70d796376d1dfbff3cdcd.zip frameworks_base-3001a035439d8134a7d70d796376d1dfbff3cdcd.tar.gz frameworks_base-3001a035439d8134a7d70d796376d1dfbff3cdcd.tar.bz2 | |
auto import from //branches/cupcake/...@132276
Diffstat (limited to 'core/java/android/webkit')
| -rw-r--r-- | core/java/android/webkit/BrowserFrame.java | 2 | ||||
| -rw-r--r-- | core/java/android/webkit/LoadListener.java | 2 | ||||
| -rw-r--r-- | core/java/android/webkit/TextDialog.java | 22 | ||||
| -rw-r--r-- | core/java/android/webkit/WebView.java | 294 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewCore.java | 99 | ||||
| -rw-r--r-- | core/java/android/webkit/gears/HttpRequestAndroid.java | 745 | ||||
| -rw-r--r-- | core/java/android/webkit/gears/UrlInterceptHandlerGears.java | 6 | 
7 files changed, 374 insertions, 796 deletions
| diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index 1dd37be..451af6d 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -51,7 +51,7 @@ class BrowserFrame extends Handler {      private final Context mContext;      private final WebViewDatabase mDatabase;      private final WebViewCore mWebViewCore; -    private boolean mLoadInitFromJava; +    /* package */ boolean mLoadInitFromJava;      private int mLoadType;      private boolean mFirstLayoutDone = true;      private boolean mCommitted = true; diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java index 3694969..dfae17d 100644 --- a/core/java/android/webkit/LoadListener.java +++ b/core/java/android/webkit/LoadListener.java @@ -575,7 +575,7 @@ class LoadListener extends Handler implements EventHandler {                                  mRequestHandle.getMethod().equals("POST")) {                          sendMessageInternal(obtainMessage(                                  MSG_LOCATION_CHANGED_REQUEST));   -                    } else if (mMethod.equals("POST"))  { +                    } else if (mMethod != null && mMethod.equals("POST")) {                          sendMessageInternal(obtainMessage(                                  MSG_LOCATION_CHANGED_REQUEST));                      } else { diff --git a/core/java/android/webkit/TextDialog.java b/core/java/android/webkit/TextDialog.java index 9af30c5..c2620a5 100644 --- a/core/java/android/webkit/TextDialog.java +++ b/core/java/android/webkit/TextDialog.java @@ -291,6 +291,25 @@ import java.util.ArrayList;      }      /** +     *  Create a fake touch up event at (x,y) with respect to this TextDialog. +     *  This is used by WebView to act as though a touch event which happened +     *  before we placed the TextDialog actually hit it, so that it can place +     *  the cursor accordingly. +     */ +    /* package */ void fakeTouchEvent(float x, float y) { +        // We need to ensure that there is a Layout, since the Layout is used +        // in determining where to place the cursor. +        if (getLayout() == null) { +            measure(mWidthSpec, mHeightSpec); +        } +        // Create a fake touch up, which is used to place the cursor. +        MotionEvent ev = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, +                x, y, 0); +        onTouchEvent(ev); +        ev.recycle(); +    } + +    /**       *  Determine whether this TextDialog currently represents the node       *  represented by ptr.       *  @param  ptr Pointer to a node to compare to. @@ -461,9 +480,8 @@ import java.util.ArrayList;       */      public void setAdapterCustom(AutoCompleteAdapter adapter) {          if (adapter != null) { -            adapter.setTextView(this); -        } else {              setInputType(EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE); +            adapter.setTextView(this);          }          super.setAdapter(adapter);      } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index bdbf38a..4d9a8fb 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -45,6 +45,8 @@ import android.util.AttributeSet;  import android.util.Config;  import android.util.Log;  import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils;  import android.view.Gravity;  import android.view.KeyEvent;  import android.view.LayoutInflater; @@ -234,6 +236,9 @@ public class WebView extends AbsoluteLayout       */      VelocityTracker mVelocityTracker; +    private static boolean mShowZoomRingTutorial = true; +    private static final int ZOOM_RING_TUTORIAL_DURATION = 3000; +      /**       * Touch mode       */ @@ -284,9 +289,7 @@ public class WebView extends AbsoluteLayout      // In the browser, if it switches out of tap too soon, jump tap won't work.      private static final int TAP_TIMEOUT = 200;      // The duration in milliseconds we will wait to see if it is a double tap. -    // With a limited survey, the time between the first tap up and the second -    // tap down in the double tap case is around 70ms - 120ms. -    private static final int DOUBLE_TAP_TIMEOUT = 200; +    private static final int DOUBLE_TAP_TIMEOUT = 250;      // This should be ViewConfiguration.getLongPressTimeout()      // But system time out is 500ms, which is too short for the browser.      // With a short timeout, it's difficult to treat trigger a short press. @@ -315,6 +318,9 @@ public class WebView extends AbsoluteLayout      private int mContentWidth;   // cache of value from WebViewCore      private int mContentHeight;  // cache of value from WebViewCore +    static int MAX_FLOAT_CONTENT_WIDTH = 480; +    private int mMinContentWidth; +      // Need to have the separate control for horizontal and vertical scrollbar       // style than the View's single scrollbar style      private boolean mOverlayHorizontalScrollbar = true; @@ -348,6 +354,7 @@ public class WebView extends AbsoluteLayout      private static final int UPDATE_TEXT_ENTRY_ADAPTER = 6;      private static final int SWITCH_TO_ENTER = 7;      private static final int RESUME_WEBCORE_UPDATE = 8; +    private static final int DISMISS_ZOOM_RING_TUTORIAL = 9;      //! arg1=x, arg2=y      static final int SCROLL_TO_MSG_ID               = 10; @@ -370,12 +377,46 @@ public class WebView extends AbsoluteLayout      static final int WEBCORE_NEED_TOUCH_EVENTS      = 25;      // obj=Rect in doc coordinates      static final int INVAL_RECT_MSG_ID              = 26; +     +    static final String[] HandlerDebugString = { +        "REMEMBER_PASSWORD", // = 1; +        "NEVER_REMEMBER_PASSWORD", // = 2; +        "SWITCH_TO_SHORTPRESS", // = 3; +        "SWITCH_TO_LONGPRESS", // = 4; +        "RELEASE_SINGLE_TAP", // = 5; +        "UPDATE_TEXT_ENTRY_ADAPTER", // = 6; +        "SWITCH_TO_ENTER", // = 7; +        "RESUME_WEBCORE_UPDATE", // = 8; +        "9", +        "SCROLL_TO_MSG_ID", //               = 10; +        "SCROLL_BY_MSG_ID", //               = 11; +        "SPAWN_SCROLL_TO_MSG_ID", //         = 12; +        "SYNC_SCROLL_TO_MSG_ID", //          = 13; +        "NEW_PICTURE_MSG_ID", //             = 14; +        "UPDATE_TEXT_ENTRY_MSG_ID", //       = 15; +        "WEBCORE_INITIALIZED_MSG_ID", //     = 16; +        "UPDATE_TEXTFIELD_TEXT_MSG_ID", //   = 17; +        "DID_FIRST_LAYOUT_MSG_ID", //        = 18; +        "RECOMPUTE_FOCUS_MSG_ID", //         = 19; +        "NOTIFY_FOCUS_SET_MSG_ID", //        = 20; +        "MARK_NODE_INVALID_ID", //           = 21; +        "UPDATE_CLIPBOARD", //               = 22; +        "LONG_PRESS_ENTER", //               = 23; +        "PREVENT_TOUCH_ID", //               = 24; +        "WEBCORE_NEED_TOUCH_EVENTS", //      = 25; +        "INVAL_RECT_MSG_ID" //               = 26; +    };      // width which view is considered to be fully zoomed out      static final int ZOOM_OUT_WIDTH = 1024; -    private static final float DEFAULT_MAX_ZOOM_SCALE = 4; -    private static final float DEFAULT_MIN_ZOOM_SCALE = 0.25f; +    private static final float MAX_ZOOM_RING_ANGLE = (float) (Math.PI * 2 / 3); +    private static final int ZOOM_RING_STEPS = 4; +    private static final float ZOOM_RING_ANGLE_UNIT = MAX_ZOOM_RING_ANGLE +            / ZOOM_RING_STEPS; + +    private static final float DEFAULT_MAX_ZOOM_SCALE = 2; +    private static final float DEFAULT_MIN_ZOOM_SCALE = (float) 1/3;      // scale limit, which can be set through viewport meta tag in the web page      private float mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;      private float mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE; @@ -505,6 +546,8 @@ public class WebView extends AbsoluteLayout      }      private ZoomRingController mZoomRingController; +    private ImageView mZoomRingOverview; +    private Animation mZoomRingOverviewExitAnimation;      // These keep track of the center point of the zoom ring.  They are used to      // determine the point around which we should zoom. @@ -519,51 +562,83 @@ public class WebView extends AbsoluteLayout              // in this callback          } +        public void onBeginPan() { +            setZoomOverviewVisible(false); +        } +          public boolean onPan(int deltaX, int deltaY) {              return pinScrollBy(deltaX, deltaY, false, 0);          } +        public void onEndPan() { +        } +          public void onVisibilityChanged(boolean visible) {              if (visible) { -                mZoomControls.show(false, canZoomScrollOut()); -            } else { -                mZoomControls.hide(); +                switchOutDrawHistory(); +                float angle = 0f; +                if (mActualScale > 1) { +                    angle = -(float) Math.round(ZOOM_RING_STEPS +                            * (mActualScale - 1) / (mMaxZoomScale - 1)) +                            / ZOOM_RING_STEPS; +                } else if (mActualScale < 1) { +                    angle = (float) Math.round(ZOOM_RING_STEPS +                            * (1 - mActualScale) / (1 - mMinZoomScale)) +                            / ZOOM_RING_STEPS; +                } +                mZoomRingController.setThumbAngle(angle * MAX_ZOOM_RING_ANGLE); +                 +                // Show the zoom overview tab on the ring +                setZoomOverviewVisible(true);              }          } -                 -        public void onBeginDrag(float startAngle) { + +        public void onBeginDrag() {              mPreviewZoomOnly = true; +            setZoomOverviewVisible(false);          } -        public void onEndDrag(float endAngle) { +        public void onEndDrag() {              mPreviewZoomOnly = false;              setNewZoomScale(mActualScale, true);          }          public boolean onDragZoom(int deltaZoomLevel, int centerX,                  int centerY, float startAngle, float curAngle) { - -            if (mZoomScale == mMinZoomScale && deltaZoomLevel < 0 || -                    mZoomScale == mMaxZoomScale && deltaZoomLevel > 0 || -                    deltaZoomLevel == 0) { +            if (deltaZoomLevel < 0 +                    && Math.abs(mActualScale - mMinZoomScale) < 0.01f +                    || deltaZoomLevel > 0 +                    &&  Math.abs(mActualScale - mMaxZoomScale) < 0.01f +                    || deltaZoomLevel == 0) {                  return false;              }              mZoomCenterX = (float) centerX;              mZoomCenterY = (float) centerY; -            while (deltaZoomLevel != 0) { -                if (deltaZoomLevel > 0) { -                    if (!zoomIn()) return false; -                    deltaZoomLevel--; +            float scale = 1.0f; +            if (curAngle > (float) Math.PI) +                curAngle -= (float) 2 * Math.PI; +            if (curAngle > 0) { +                if (curAngle >= MAX_ZOOM_RING_ANGLE) { +                    scale = mMinZoomScale; +                } else { +                    scale = 1 - (float) Math.round(curAngle +                            / ZOOM_RING_ANGLE_UNIT) / ZOOM_RING_STEPS +                            * (1 - mMinZoomScale); +                } +            } else if (curAngle < 0) { +                if (curAngle <= -MAX_ZOOM_RING_ANGLE) { +                    scale = mMaxZoomScale;                  } else { -                    if (!zoomOut()) return false; -                    deltaZoomLevel++; +                    scale = 1 + (float) Math.round(-curAngle +                            / ZOOM_RING_ANGLE_UNIT) / ZOOM_RING_STEPS +                            * (mMaxZoomScale - 1);                  }              } - +            zoomWithPreview(scale);              return true;          } -         +          public void onSimpleZoom(boolean zoomIn) {              if (zoomIn) {                  zoomIn(); @@ -571,6 +646,7 @@ public class WebView extends AbsoluteLayout                  zoomOut();              }          } +              };      /** @@ -610,7 +686,14 @@ public class WebView extends AbsoluteLayout          mFocusData.mY = 0;          mScroller = new Scroller(context);          mZoomRingController = new ZoomRingController(context, this); +        mZoomRingController.setResetThumbAutomatically(false); +        mZoomRingController.setThumbClockwiseBound( +                (float) (2 * Math.PI - MAX_ZOOM_RING_ANGLE)); +        mZoomRingController.setThumbCounterclockwiseBound(MAX_ZOOM_RING_ANGLE);          mZoomRingController.setCallback(mZoomListener); +        mZoomRingController.setZoomRingTrack( +                com.android.internal.R.drawable.zoom_ring_track_absolute); +        createZoomRingOverviewTab();      }      private void init() { @@ -625,6 +708,63 @@ public class WebView extends AbsoluteLayout          mMinLockSnapReverseDistance = slop;      } +    private void createZoomRingOverviewTab() { +        Context context = getContext(); +         +        mZoomRingOverviewExitAnimation = AnimationUtils.loadAnimation(context, +                com.android.internal.R.anim.fade_out); +         +        mZoomRingOverview = new ImageView(context); +        mZoomRingOverview.setBackgroundResource( +                com.android.internal.R.drawable.zoom_ring_overview_tab); +        mZoomRingOverview.setImageResource(com.android.internal.R.drawable.btn_zoom_page); +         +        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( +                FrameLayout.LayoutParams.WRAP_CONTENT, +                FrameLayout.LayoutParams.WRAP_CONTENT, +                Gravity.CENTER); +        // TODO: magic constant that's based on the zoom ring radius + some offset +        lp.topMargin = 208; +        mZoomRingOverview.setLayoutParams(lp); +        mZoomRingOverview.setOnClickListener(new View.OnClickListener() { +            public void onClick(View v) { +                // Hide the zoom ring +                mZoomRingController.setVisible(false); +                zoomScrollOut(); +            }}); +         +        // Measure the overview View to figure out its height +        mZoomRingOverview.forceLayout(); +        mZoomRingOverview.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), +                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); +         +        ViewGroup container = mZoomRingController.getContainer(); +        // Find the index of the zoom ring in the container +        View zoomRing = container.findViewById(mZoomRingController.getZoomRingId()); +        int zoomRingIndex; +        for (zoomRingIndex = container.getChildCount() - 1; zoomRingIndex >= 0; zoomRingIndex--) { +            if (container.getChildAt(zoomRingIndex) == zoomRing) break; +        } +        // Add the overview tab below the zoom ring (so we don't steal its events) +        container.addView(mZoomRingOverview, zoomRingIndex); +        // Since we use margins to adjust the vertical placement of the tab, the widget +        // ends up getting clipped off. Ensure the container is big enough for +        // us. +        int myHeight = mZoomRingOverview.getMeasuredHeight() + lp.topMargin / 2; +        // Multiplied by 2 b/c the zoom ring needs to be centered on the screen +        container.setMinimumHeight(myHeight * 2); +    } +     +    private void setZoomOverviewVisible(boolean visible) { +        int newVisibility = visible ? View.VISIBLE : View.INVISIBLE; +        if (mZoomRingOverview.getVisibility() == newVisibility) return; +             +        if (!visible) { +            mZoomRingOverview.startAnimation(mZoomRingOverviewExitAnimation); +        } +        mZoomRingOverview.setVisibility(newVisibility); +    } +          /* package */ boolean onSavePassword(String schemePlusHost, String username,              String password, final Message resumeMsg) {         boolean rVal = false; @@ -1653,7 +1793,8 @@ public class WebView extends AbsoluteLayout       * @return true if new values were sent       */      private boolean sendViewSizeZoom() { -        int newWidth = Math.round(getViewWidth() * mInvActualScale); +        int viewWidth = getViewWidth(); +        int newWidth = Math.round(viewWidth * mInvActualScale);          int newHeight = Math.round(getViewHeight() * mInvActualScale);          /*           * Because the native side may have already done a layout before the @@ -1669,7 +1810,7 @@ public class WebView extends AbsoluteLayout          // 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)); +                    newWidth, newHeight, new Integer(viewWidth));              mLastWidthSent = newWidth;              mLastHeightSent = newHeight;              return true; @@ -1968,7 +2109,7 @@ public class WebView extends AbsoluteLayout      // Scale from content to view coordinates, and pin.      // Also called by jni webview.cpp -    private void setContentScrollBy(int cx, int cy) { +    private void setContentScrollBy(int cx, int cy, boolean animate) {          if (mDrawHistory) {              // disallow WebView to change the scroll position as History Picture              // is used in the view system. @@ -1992,10 +2133,10 @@ public class WebView extends AbsoluteLayout              // vertical scroll?  //                Log.d(LOGTAG, "setContentScrollBy cy=" + cy);              if (cy == 0 && cx != 0) { -                pinScrollBy(cx, 0, true, 0); +                pinScrollBy(cx, 0, animate, 0);              }          } else { -            pinScrollBy(cx, cy, true, 0); +            pinScrollBy(cx, cy, animate, 0);          }      } @@ -2205,7 +2346,8 @@ public class WebView extends AbsoluteLayout              // state.              // If mNativeClass is 0, we should not reach here, so we do not              // need to check it again. -            nativeRecordButtons(mTouchMode == TOUCH_SHORTPRESS_START_MODE +            nativeRecordButtons(hasFocus() && hasWindowFocus(), +                    mTouchMode == TOUCH_SHORTPRESS_START_MODE                      || mTrackballDown || mGotEnterDown, false);              drawCoreAndFocusRing(canvas, mBackgroundColor, mDrawFocusRing);          } @@ -2254,6 +2396,8 @@ public class WebView extends AbsoluteLayout                  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); @@ -2775,6 +2919,17 @@ public class WebView extends AbsoluteLayout                  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 - lp.x; +            float y = mLastTouchY - lp.y; +            mTextEntry.fakeTouchEvent(x, y); +        }      }      private void updateTextEntry() { @@ -2987,7 +3142,9 @@ public class WebView extends AbsoluteLayout                  mGotEnterDown = true;                  mPrivateHandler.sendMessageDelayed(mPrivateHandler                          .obtainMessage(LONG_PRESS_ENTER), LONG_PRESS_TIMEOUT); -                nativeRecordButtons(true, true); +                // 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 @@ -3198,6 +3355,9 @@ public class WebView extends AbsoluteLayout              ViewGroup p = (ViewGroup) parent;              p.setOnHierarchyChangeListener(null);          } + +        // Clean up the zoom ring +        mZoomRingController.setVisible(false);      }      // Implementation for OnHierarchyChangeListener @@ -3234,16 +3394,25 @@ public class WebView extends AbsoluteLayout                  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 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); @@ -3264,12 +3433,22 @@ public class WebView extends AbsoluteLayout                      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;          } @@ -3285,6 +3464,22 @@ public class WebView extends AbsoluteLayout          // the new zoom ring controller          mZoomCenterX = getViewWidth() * .5f;          mZoomCenterY = getViewHeight() * .5f; + +        // update mMinZoomScale +        if (mMinContentWidth > MAX_FLOAT_CONTENT_WIDTH) { +            boolean atMin = Math.abs(mActualScale - mMinZoomScale) < 0.01f; +            mMinZoomScale = (float) getViewWidth() / mMinContentWidth; +            if (atMin) { +                // if the WebView was at the minimum zoom scale, keep it. e,g., +                // the WebView was at the minimum zoom scale at the portrait +                // mode, rotate it to the landscape modifying the scale to the +                // new minimum zoom scale, when rotating back, we would like to +                // keep the minimum zoom scale instead of keeping the same scale +                // as normally we do. +                mActualScale = mMinZoomScale; +            } +        } +          // we always force, in case our height changed, in which case we still          // want to send the notification over to webkit          setNewZoomScale(mActualScale, true); @@ -3340,6 +3535,14 @@ public class WebView extends AbsoluteLayout              return false;          } +        if (mShowZoomRingTutorial && mMinZoomScale < mMaxZoomScale) { +            ZoomRingController.showZoomTutorialOnce(mContext); +            mShowZoomRingTutorial = false; +            mPrivateHandler.sendMessageDelayed(mPrivateHandler +                    .obtainMessage(DISMISS_ZOOM_RING_TUTORIAL), +                    ZOOM_RING_TUTORIAL_DURATION); +        } +          if (LOGV_ENABLED) {              Log.v(LOGTAG, ev + " at " + ev.getEventTime() + " mTouchMode="                      + mTouchMode); @@ -3749,7 +3952,7 @@ public class WebView extends AbsoluteLayout              mPrivateHandler.removeMessages(SWITCH_TO_ENTER);              mTrackballDown = true;              if (mNativeClass != 0) { -                nativeRecordButtons(true, true); +                nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);              }              if (time - mLastFocusTime <= TRACKBALL_TIMEOUT                      && !mLastFocusBounds.equals(nativeGetFocusRingBounds())) { @@ -4148,6 +4351,9 @@ public class WebView extends AbsoluteLayout          });          zoomControls.setOnZoomMagnifyClickListener(new OnClickListener() {              public void onClick(View v) { +                // Hide the zoom ring +                mZoomRingController.setVisible(false); +                                  mPrivateHandler.removeCallbacks(mZoomControlRunnable);                  mPrivateHandler.postDelayed(mZoomControlRunnable,                          ZOOM_CONTROLS_TIMEOUT); @@ -4200,6 +4406,12 @@ public class WebView extends AbsoluteLayout          }      } +    // Called by JNI to handle a touch on a node representing an email address, +    // address, or phone number +    private void overrideLoading(String url) { +        mCallbackProxy.uiOverrideUrlLoading(url); +    } +      @Override      public boolean requestFocus(int direction, Rect previouslyFocusedRect) {          boolean result = false; @@ -4372,6 +4584,11 @@ public class WebView extends AbsoluteLayout      class PrivateHandler extends Handler {          @Override          public void handleMessage(Message msg) { +            if (LOGV_ENABLED) { +                Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what  +                        > INVAL_RECT_MSG_ID ? Integer.toString(msg.what)  +                        : HandlerDebugString[msg.what - REMEMBER_PASSWORD]); +            }              switch (msg.what) {                  case REMEMBER_PASSWORD: {                      mDatabase.setUsernamePassword( @@ -4413,7 +4630,7 @@ public class WebView extends AbsoluteLayout                              , KeyEvent.KEYCODE_ENTER));                      break;                  case SCROLL_BY_MSG_ID: -                    setContentScrollBy(msg.arg1, msg.arg2); +                    setContentScrollBy(msg.arg1, msg.arg2, (Boolean) msg.obj);                      break;                  case SYNC_SCROLL_TO_MSG_ID:                      if (mUserScroll) { @@ -4451,6 +4668,11 @@ public class WebView extends AbsoluteLayout                                      0, 0);                          }                      } +                    mMinContentWidth = msg.arg1; +                    if (mMinContentWidth > MAX_FLOAT_CONTENT_WIDTH) { +                        mMinZoomScale = (float) getViewWidth() +                                / mMinContentWidth; +                    }                      // We update the layout (i.e. request a layout from the                      // view system) if the last view size that we sent to                      // WebCore matches the view size of the picture we just @@ -4638,6 +4860,10 @@ public class WebView extends AbsoluteLayout                      }                      break; +                case DISMISS_ZOOM_RING_TUTORIAL: +                    mZoomRingController.finishZoomTutorial(); +                    break; +                  default:                      super.handleMessage(msg);                      break; @@ -5018,8 +5244,8 @@ public class WebView extends AbsoluteLayout      private native void     nativeRecomputeFocus();      // Like many other of our native methods, you must make sure that      // mNativeClass is not null before calling this method. -    private native void     nativeRecordButtons(boolean pressed, -            boolean invalidate); +    private native void     nativeRecordButtons(boolean focused, +            boolean pressed, boolean invalidate);      private native void     nativeResetFocus();      private native void     nativeResetNavClipBounds();      private native void     nativeSelectBestAt(Rect rect); diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index b979032..45113ab 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -271,6 +271,11 @@ final class WebViewCore {      static native String nativeFindAddress(String addr);      /** +     * Rebuild the nav cache if the dom changed. +     */ +    private native void nativeCheckNavCache(); + +    /**       * Empty the picture set.       */      private native void nativeClearContent(); @@ -317,7 +322,7 @@ final class WebViewCore {          should this be called nativeSetViewPortSize?      */      private native void nativeSetSize(int width, int height, int screenWidth, -            float scale); +            float scale, int realScreenWidth, int screenHeight);      private native int nativeGetContentMinPrefWidth(); @@ -501,6 +506,51 @@ final class WebViewCore {          int mY;      } +        static final String[] HandlerDebugString = { +            "LOAD_URL", // = 100; +            "STOP_LOADING", // = 101; +            "RELOAD", // = 102; +            "KEY_DOWN", // = 103; +            "KEY_UP", // = 104; +            "VIEW_SIZE_CHANGED", // = 105; +            "GO_BACK_FORWARD", // = 106; +            "SET_SCROLL_OFFSET", // = 107; +            "RESTORE_STATE", // = 108; +            "PAUSE_TIMERS", // = 109; +            "RESUME_TIMERS", // = 110; +            "CLEAR_CACHE", // = 111; +            "CLEAR_HISTORY", // = 112; +            "SET_SELECTION", // = 113; +            "REPLACE_TEXT", // = 114; +            "PASS_TO_JS", // = 115; +            "SET_GLOBAL_BOUNDS", // = 116; +            "UPDATE_CACHE_AND_TEXT_ENTRY", // = 117; +            "CLICK", // = 118; +            "119", +            "DOC_HAS_IMAGES", // = 120; +            "SET_SNAP_ANCHOR", // = 121; +            "DELETE_SELECTION", // = 122; +            "LISTBOX_CHOICES", // = 123; +            "SINGLE_LISTBOX_CHOICE", // = 124; +            "125", +            "SET_BACKGROUND_COLOR", // = 126; +            "UNBLOCK_FOCUS", // = 127; +            "SAVE_DOCUMENT_STATE", // = 128; +            "GET_SELECTION", // = 129; +            "WEBKIT_DRAW", // = 130; +            "SYNC_SCROLL", // = 131; +            "REFRESH_PLUGINS", // = 132; +            "SPLIT_PICTURE_SET", // = 133; +            "CLEAR_CONTENT", // = 134; +            "SET_FINAL_FOCUS", // = 135; +            "SET_KIT_FOCUS", // = 136; +            "REQUEST_FOCUS_HREF", // = 137; +            "ADD_JS_INTERFACE", // = 138; +            "LOAD_DATA", // = 139; +            "TOUCH_UP", // = 140; +            "TOUCH_EVENT", // = 141; +        }; +      class EventHub {          // Message Ids          static final int LOAD_URL = 100; @@ -595,6 +645,11 @@ final class WebViewCore {              mHandler = new Handler() {                  @Override                  public void handleMessage(Message msg) { +                    if (LOGV_ENABLED) { +                        Log.v(LOGTAG, msg.what < LOAD_URL || msg.what  +                                > TOUCH_EVENT ? Integer.toString(msg.what) +                                : HandlerDebugString[msg.what - LOAD_URL]); +                    }                      switch (msg.what) {                          case WEBKIT_DRAW:                              webkitDraw(); @@ -675,7 +730,7 @@ final class WebViewCore {                          case VIEW_SIZE_CHANGED:                              viewSizeChanged(msg.arg1, msg.arg2, -                                    ((Float) msg.obj).floatValue()); +                                    ((Integer) msg.obj).intValue());                              break;                          case SET_SCROLL_OFFSET: @@ -1131,12 +1186,22 @@ final class WebViewCore {      private int mCurrentViewWidth = 0;      private int mCurrentViewHeight = 0; +    // Define a minimum screen width so that we won't wrap the paragraph to one +    // word per line during zoom-in. +    private static final int MIN_SCREEN_WIDTH = 160; +      // notify webkit that our virtual view size changed size (after inv-zoom) -    private void viewSizeChanged(int w, int h, float scale) { +    private void viewSizeChanged(int w, int h, int viewWidth) {          if (LOGV_ENABLED) Log.v(LOGTAG, "CORE onSizeChanged"); +        if (w == 0) { +            Log.w(LOGTAG, "skip viewSizeChanged as w is 0"); +            return; +        } +        float scale = (float) viewWidth / w;          if (mSettings.getUseWideViewPort()                  && (w < mViewportWidth || mViewportWidth == -1)) {              int width = mViewportWidth; +            int screenWidth = Math.max(w, MIN_SCREEN_WIDTH);              if (mViewportWidth == -1) {                  if (mSettings.getLayoutAlgorithm() ==                           WebSettings.LayoutAlgorithm.NORMAL) { @@ -1154,12 +1219,21 @@ final class WebViewCore {                       * In the worse case, the native width will be adjusted when                       * next zoom or screen orientation change happens.                       */ -                    width = Math.max(w, nativeGetContentMinPrefWidth()); +                    int minContentWidth = nativeGetContentMinPrefWidth(); +                    if (minContentWidth > WebView.MAX_FLOAT_CONTENT_WIDTH) { +                        // keep the same width and screen width so that there is  +                        // no reflow when zoom-out +                        width = minContentWidth; +                        screenWidth = Math.min(screenWidth, viewWidth); +                    } else { +                        width = Math.max(w, minContentWidth); +                    }                  }              } -            nativeSetSize(width, Math.round((float) width * h / w), w, scale); +            nativeSetSize(width, Math.round((float) width * h / w), +                    screenWidth, scale, w, h);          } else { -            nativeSetSize(w, h, w, scale); +            nativeSetSize(w, h, w, scale, w, h);          }          // Remember the current width and height          boolean needInvalidate = (mCurrentViewWidth == 0); @@ -1219,7 +1293,9 @@ final class WebViewCore {              draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);              if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");              Message.obtain(mWebView.mPrivateHandler, -                    WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget(); +                    WebView.NEW_PICTURE_MSG_ID, nativeGetContentMinPrefWidth(), +                    0, draw).sendToTarget(); +            nativeCheckNavCache();              if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {                  // as we have the new picture, try to sync the scroll position                  Message.obtain(mWebView.mPrivateHandler, @@ -1324,7 +1400,9 @@ final class WebViewCore {          for (int i = 0; i < size; i++) {              list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);          } +        mBrowserFrame.mLoadInitFromJava = true;          list.restoreIndex(mBrowserFrame.mNativeFrame, index); +        mBrowserFrame.mLoadInitFromJava = false;      }      //------------------------------------------------------------------------- @@ -1349,14 +1427,15 @@ final class WebViewCore {      }      // called by JNI -    private void contentScrollBy(int dx, int dy) { +    private void contentScrollBy(int dx, int dy, boolean animate) {          if (!mBrowserFrame.firstLayoutDone()) {              // Will this happen? If yes, we need to do something here.              return;          }          if (mWebView != null) {              Message.obtain(mWebView.mPrivateHandler, -                    WebView.SCROLL_BY_MSG_ID, dx, dy).sendToTarget(); +                    WebView.SCROLL_BY_MSG_ID, dx, dy,  +                    new Boolean(animate)).sendToTarget();          }      } @@ -1461,7 +1540,7 @@ final class WebViewCore {              // current scale              mEventHub.sendMessage(Message.obtain(null,                      EventHub.VIEW_SIZE_CHANGED, mWebView.mLastWidthSent, -                    mWebView.mLastHeightSent, -1.0f)); +                    mWebView.mLastHeightSent, new Integer(-1)));          }          mBrowserFrame.didFirstLayout(); diff --git a/core/java/android/webkit/gears/HttpRequestAndroid.java b/core/java/android/webkit/gears/HttpRequestAndroid.java deleted file mode 100644 index 30f855f..0000000 --- a/core/java/android/webkit/gears/HttpRequestAndroid.java +++ /dev/null @@ -1,745 +0,0 @@ -// Copyright 2008, The Android Open Source Project -// -// Redistribution and use in source and binary forms, with or without  -// modification, are permitted provided that the following conditions are met: -// -//  1. Redistributions of source code must retain the above copyright notice,  -//     this list of conditions and the following disclaimer. -//  2. Redistributions in binary form must reproduce the above copyright notice, -//     this list of conditions and the following disclaimer in the documentation -//     and/or other materials provided with the distribution. -//  3. Neither the name of Google Inc. nor the names of its contributors may be -//     used to endorse or promote products derived from this software without -//     specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF  -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR  -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF  -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -package android.webkit.gears; - -import android.net.http.Headers; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.util.Log; -import android.webkit.CacheManager; -import android.webkit.CacheManager.CacheResult; -import android.webkit.CookieManager; - -import org.apache.http.conn.ssl.StrictHostnameVerifier; -import org.apache.http.impl.cookie.DateUtils; -import org.apache.http.util.CharArrayBuffer; - -import java.io.*; -import java.net.*; -import java.util.*; -import javax.net.ssl.*; - -/** - * Performs the underlying HTTP/HTTPS GET and POST requests. - * <p> These are performed synchronously (blocking). The caller should - * ensure that it is in a background thread if asynchronous behavior - * is required. All data is pushed, so there is no need for JNI native - * callbacks. - * <p> This uses the java.net.HttpURLConnection class to perform most - * of the underlying network activity. The Android brower's cache, - * android.webkit.CacheManager, is also used when caching is enabled, - * and updated with new data. The android.webkit.CookieManager is also - * queried and updated as necessary. - * <p> The public interface is designed to be called by native code - * through JNI, and to simplify coding none of the public methods will - * surface a checked exception. Unchecked exceptions may still be - * raised but only if the system is in an ill state, such as out of - * memory. - * <p> TODO: This isn't plumbed into LocalServer yet. Mutually - * dependent on LocalServer - will attach the two together once both - * are submitted. - */ -public final class HttpRequestAndroid { -  /** Debug logging tag. */ -  private static final String LOG_TAG = "Gears-J"; -  /** HTTP response header line endings are CR-LF style. */ -  private static final String HTTP_LINE_ENDING = "\r\n"; -  /** Safe MIME type to use whenever it isn't specified. */ -  private static final String DEFAULT_MIME_TYPE = "text/plain"; -  /** Case-sensitive header keys */ -  public static final String KEY_CONTENT_LENGTH = "Content-Length"; -  public static final String KEY_EXPIRES = "Expires"; -  public static final String KEY_LAST_MODIFIED = "Last-Modified"; -  public static final String KEY_ETAG = "ETag"; -  public static final String KEY_LOCATION = "Location"; -  public static final String KEY_CONTENT_TYPE = "Content-Type"; -  /** Number of bytes to send and receive on the HTTP connection in -   * one go. */ -  private static final int BUFFER_SIZE = 4096; -  /** The first element of the String[] value in a headers map is the -   * unmodified (case-sensitive) key. */ -  public static final int HEADERS_MAP_INDEX_KEY = 0; -  /** The second element of the String[] value in a headers map is the -   * associated value. */ -  public static final int HEADERS_MAP_INDEX_VALUE = 1; - -  /** Enable/disable all logging in this class. */ -  private static boolean logEnabled = false; -  /** The underlying HTTP or HTTPS network connection. */ -  private HttpURLConnection connection; -  /** HTTP body stream, setup after connection. */ -  private InputStream inputStream; -  /** The complete response line e.g "HTTP/1.0 200 OK" */ -  private String responseLine; -  /** Request headers, as a lowercase key -> [ unmodified key, value ] map. */ -  private Map<String, String[]> requestHeaders = -      new HashMap<String, String[]>(); -  /** Response headers, as a lowercase key -> [ unmodified key, value ] map. */ -  private Map<String, String[]> responseHeaders; -  /** True if the child thread is in performing blocking IO. */ -  private boolean inBlockingOperation = false; -  /** True when the thread acknowledges the abort. */ -  private boolean abortReceived = false; -  /** The URL used for createCacheResult() */ -  private String cacheResultUrl; -  /** CacheResult being saved into, if inserting a new cache entry. */ -  private CacheResult cacheResult; -  /** Initialized by initChildThread(). Used to target abort(). */ -  private Thread childThread; - -  /** -   * Convenience debug function. Calls Android logging mechanism. -   * @param str String to log to the Android console. -   */ -  private static void log(String str) { -    if (logEnabled) { -      Log.i(LOG_TAG, str); -    } -  } - -  /** -   * Turn on/off logging in this class. -   * @param on Logging enable state. -   */ -  public static void enableLogging(boolean on) { -    logEnabled = on; -  } - -  /** -   * Initialize childThread using the TLS value of -   * Thread.currentThread(). Called on start up of the native child -   * thread. -   */ -  public synchronized void initChildThread() { -    childThread = Thread.currentThread(); -  } - -  /** -   * Analagous to the native-side HttpRequest::open() function. This -   * initializes an underlying java.net.HttpURLConnection, but does -   * not go to the wire. On success, this enables a call to send() to -   * initiate the transaction. -   * -   * @param method    The HTTP method, e.g GET or POST. -   * @param url       The URL to open. -   * @return          True on success with a complete HTTP response. -   *                  False on failure. -   */ -  public synchronized boolean open(String method, String url) { -    if (logEnabled) -      log("open " + method + " " + url); -    // Reset the response between calls to open(). -    inputStream = null; -    responseLine = null; -    responseHeaders = null; -    if (!method.equals("GET") && !method.equals("POST")) { -      log("Method " + method + " not supported"); -      return false; -    } -    // Setup the connection. This doesn't go to the wire yet - it -    // doesn't block. -    try { -      URL url_object = new URL(url); -      // Check that the protocol is indeed HTTP(S). -      String protocol = url_object.getProtocol(); -      if (protocol == null) { -        log("null protocol for URL " + url); -        return false; -      } -      protocol = protocol.toLowerCase(); -      if (!"http".equals(protocol) && !"https".equals(protocol)) { -        log("Url has wrong protocol: " + url); -        return false; -      } - -      connection = (HttpURLConnection) url_object.openConnection(); -      connection.setRequestMethod(method); -      // Manually follow redirects. -      connection.setInstanceFollowRedirects(false); -      // Manually cache. -      connection.setUseCaches(false); -      // Enable data output in POST method requests. -      connection.setDoOutput(method.equals("POST")); -      // Enable data input in non-HEAD method requests. -      // TODO: HEAD requests not tested. -      connection.setDoInput(!method.equals("HEAD")); -      if (connection instanceof javax.net.ssl.HttpsURLConnection) { -        // Verify the certificate matches the origin. -        ((HttpsURLConnection) connection).setHostnameVerifier( -            new StrictHostnameVerifier()); -      } -      return true; -    } catch (IOException e) { -      log("Got IOException in open: " + e.toString()); -      return false; -    } -  } - -  /** -   * Interrupt a blocking IO operation. This will cause the child -   * thread to expediently return from an operation if it was stuck at -   * the time. Note that this inherently races, and unfortunately -   * requires the caller to loop. -   */ -  public synchronized void interrupt() { -    if (childThread == null) { -      log("interrupt() called but no child thread"); -      return; -    } -    synchronized (this) { -      if (inBlockingOperation) { -        log("Interrupting blocking operation"); -        childThread.interrupt(); -      } else { -        log("Nothing to interrupt"); -      } -    } -  } - -  /** -   * Set a header to send with the HTTP request. Will not take effect -   * on a transaction already in progress. The key is associated -   * case-insensitive, but stored case-sensitive. -   * @param name  The name of the header, e.g "Set-Cookie". -   * @param value The value for this header, e.g "text/html". -   */ -  public synchronized void setRequestHeader(String name, String value) { -    String[] mapValue = { name, value }; -    requestHeaders.put(name.toLowerCase(), mapValue); -  } - -  /** -   * Returns the value associated with the given request header. -   * @param name The name of the request header, non-null, case-insensitive. -   * @return The value associated with the request header, or null if -   *         not set, or error. -   */ -  public synchronized String getRequestHeader(String name) { -    String[] value = requestHeaders.get(name.toLowerCase()); -    if (value != null) { -      return value[HEADERS_MAP_INDEX_VALUE]; -    } else { -      return null; -    } -  } - -  /** -   * Returns the value associated with the given response header. -   * @param name The name of the response header, non-null, case-insensitive. -   * @return The value associated with the response header, or null if -   *         not set or error. -   */ -  public synchronized String getResponseHeader(String name) { -    if (responseHeaders != null) { -      String[] value = responseHeaders.get(name.toLowerCase()); -      if (value != null) { -        return value[HEADERS_MAP_INDEX_VALUE]; -      } else { -        return null; -      } -    } else { -      log("getResponseHeader() called but response not received"); -      return null; -    }  -  } - -  /** -   * Set a response header and associated value. The key is associated -   * case-insensitively, but stored case-sensitively. -   * @param name  Case sensitive request header key. -   * @param value The associated value. -   */ -  private void setResponseHeader(String name, String value) { -    if (logEnabled) -      log("Set response header " + name + ": " + value); -    String mapValue[] = { name, value }; -    responseHeaders.put(name.toLowerCase(), mapValue); -  } -   -  /** -   * Apply the contents of the Map requestHeaders to the connection -   * object. Calls to setRequestHeader() after this will not affect -   * the connection. -   */ -  private synchronized void applyRequestHeadersToConnection() { -    Iterator<String[]> it = requestHeaders.values().iterator(); -    while (it.hasNext()) { -      // Set the key case-sensitive. -      String[] entry = it.next(); -      connection.setRequestProperty( -          entry[HEADERS_MAP_INDEX_KEY], -          entry[HEADERS_MAP_INDEX_VALUE]); -    } -  } - -  /** -   * Return all response headers, separated by CR-LF line endings, and -   * ending with a trailing blank line. This mimics the format of the -   * raw response header up to but not including the body. -   * @return A string containing the entire response header. -   */ -  public synchronized String getAllResponseHeaders() { -    if (responseHeaders == null) { -      log("getAllResponseHeaders() called but response not received"); -      return null; -    } -    String result = new String(); -    Iterator<String[]> it = responseHeaders.values().iterator(); -    while (it.hasNext()) { -      String[] entry = it.next(); -      // Output the "key: value" lines. -      result += entry[HEADERS_MAP_INDEX_KEY] + ": " -          + entry[HEADERS_MAP_INDEX_VALUE] + HTTP_LINE_ENDING; -    } -    result += HTTP_LINE_ENDING; -    return result; -  } - -  /** -   * Get the complete response line of the HTTP request. Only valid on -   * completion of the transaction. -   * @return The complete HTTP response line, e.g "HTTP/1.0 200 OK". -   */ -  public synchronized String getResponseLine() { -    return responseLine; -  } - -  /** -   * Get the cookie for the given URL. -   * @param url The fully qualified URL. -   * @return A string containing the cookie for the URL if it exists, -   *         or null if not. -   */ -  public static String getCookieForUrl(String url) { -    // Get the cookie for this URL, set as a header -    return CookieManager.getInstance().getCookie(url); -  } - -  /** -   * Set the cookie for the given URL. -   * @param url    The fully qualified URL. -   * @param cookie The new cookie value. -   * @return A string containing the cookie for the URL if it exists, -   *         or null if not. -   */ -  public static void setCookieForUrl(String url, String cookie) { -    // Get the cookie for this URL, set as a header -    CookieManager.getInstance().setCookie(url, cookie); -  } - -  /** -   * Perform a request using LocalServer if possible. Initializes -   * class members so that receive() will obtain data from the stream -   * provided by the response. -   * @param url The fully qualified URL to try in LocalServer. -   * @return True if the url was found and is now setup to receive. -   *         False if not found, with no side-effect. -   */ -  public synchronized boolean useLocalServerResult(String url) { -    UrlInterceptHandlerGears handler = UrlInterceptHandlerGears.getInstance(); -    if (handler == null) { -      return false; -    } -    UrlInterceptHandlerGears.ServiceResponse serviceResponse = -        handler.getServiceResponse(url, requestHeaders); -    if (serviceResponse == null) { -      log("No response in LocalServer"); -      return false; -    } -    // LocalServer will handle this URL. Initialize stream and -    // response. -    inputStream = serviceResponse.getInputStream(); -    responseLine = serviceResponse.getStatusLine(); -    responseHeaders = serviceResponse.getResponseHeaders(); -    if (logEnabled) -      log("Got response from LocalServer: " + responseLine); -    return true; -  } - -  /** -   * Perform a request using the cache result if present. Initializes -   * class members so that receive() will obtain data from the cache. -   * @param url The fully qualified URL to try in the cache. -   * @return True is the url was found and is now setup to receive -   *         from cache. False if not found, with no side-effect. -   */ -  public synchronized boolean useCacheResult(String url) { -    // Try the browser's cache. CacheManager wants a Map<String, String>. -    Map<String, String> cacheRequestHeaders = new HashMap<String, String>(); -    Iterator<Map.Entry<String, String[]>> it = -        requestHeaders.entrySet().iterator(); -    while (it.hasNext()) { -      Map.Entry<String, String[]> entry = it.next(); -      cacheRequestHeaders.put( -          entry.getKey(), -          entry.getValue()[HEADERS_MAP_INDEX_VALUE]); -    } -    CacheResult cacheResult = -        CacheManager.getCacheFile(url, cacheRequestHeaders); -    if (cacheResult == null) { -      if (logEnabled) -        log("No CacheResult for " + url); -      return false; -    } -    if (logEnabled) -      log("Got CacheResult from browser cache"); -    // Check for expiry. -1 is "never", otherwise milliseconds since 1970. -    // Can be compared to System.currentTimeMillis(). -    long expires = cacheResult.getExpires(); -    if (expires >= 0 && System.currentTimeMillis() >= expires) { -      log("CacheResult expired " -          + (System.currentTimeMillis() - expires) -          + " milliseconds ago"); -      // Cache hit has expired. Do not return it. -      return false; -    } -    // Setup the inputStream to come from the cache. -    inputStream = cacheResult.getInputStream(); -    if (inputStream == null) { -      // Cache result may have gone away. -      log("No inputStream for CacheResult " + url); -      return false; -    } -    // Cache hit. Parse headers. -    synthesizeHeadersFromCacheResult(cacheResult); -    return true; -  } - -  /** -   * Take the limited set of headers in a CacheResult and synthesize -   * response headers. -   * @param cacheResult A CacheResult to populate responseHeaders with. -   */ -  private void synthesizeHeadersFromCacheResult(CacheResult cacheResult) { -    int statusCode = cacheResult.getHttpStatusCode(); -    // The status message is informal, so we can greatly simplify it. -    String statusMessage; -    if (statusCode >= 200 && statusCode < 300) { -      statusMessage = "OK"; -    } else if (statusCode >= 300 && statusCode < 400) { -      statusMessage = "MOVED"; -    } else { -      statusMessage = "UNAVAILABLE"; -    } -    // Synthesize the response line. -    responseLine = "HTTP/1.1 " + statusCode + " " + statusMessage; -    if (logEnabled) -      log("Synthesized " + responseLine); -    // Synthesize the returned headers from cache. -    responseHeaders = new HashMap<String, String[]>(); -    String contentLength = Long.toString(cacheResult.getContentLength()); -    setResponseHeader(KEY_CONTENT_LENGTH, contentLength); -    long expires = cacheResult.getExpires(); -    if (expires >= 0) { -      // "Expires" header is valid and finite. Milliseconds since 1970 -      // epoch, formatted as RFC-1123. -      String expiresString = DateUtils.formatDate(new Date(expires)); -      setResponseHeader(KEY_EXPIRES, expiresString); -    } -    String lastModified = cacheResult.getLastModified(); -    if (lastModified != null) { -      // Last modification time of the page. Passed end-to-end, but -      // not used by us. -      setResponseHeader(KEY_LAST_MODIFIED, lastModified); -    } -    String eTag = cacheResult.getETag(); -    if (eTag != null) { -      // Entity tag. A kind of GUID to identify identical resources. -      setResponseHeader(KEY_ETAG, eTag); -    } -    String location = cacheResult.getLocation(); -    if (location != null) { -      // If valid, refers to the location of a redirect. -      setResponseHeader(KEY_LOCATION, location); -    } -    String mimeType = cacheResult.getMimeType(); -    if (mimeType == null) { -      // Use a safe default MIME type when none is -      // specified. "text/plain" is safe to render in the browser -      // window (even if large) and won't be intepreted as anything -      // that would cause execution. -      mimeType = DEFAULT_MIME_TYPE; -    } -    String encoding = cacheResult.getEncoding(); -    // Encoding may not be specified. No default. -    String contentType = mimeType; -    if (encoding != null && encoding.length() > 0) { -      contentType += "; charset=" + encoding; -    } -    setResponseHeader(KEY_CONTENT_TYPE, contentType); -  } - -  /** -   * Create a CacheResult for this URL. This enables the repsonse body -   * to be sent in calls to appendCacheResult(). -   * @param url          The fully qualified URL to add to the cache. -   * @param responseCode The response code returned for the request, e.g 200. -   * @param mimeType     The MIME type of the body, e.g "text/plain". -   * @param encoding     The encoding, e.g "utf-8". Use "" for unknown. -   */ -  public synchronized boolean createCacheResult( -      String url, int responseCode, String mimeType, String encoding) { -    if (logEnabled) -      log("Making cache entry for " + url); -    // Take the headers and parse them into a format needed by -    // CacheManager. -    Headers cacheHeaders = new Headers(); -    Iterator<Map.Entry<String, String[]>> it = -        responseHeaders.entrySet().iterator(); -    while (it.hasNext()) { -      Map.Entry<String, String[]> entry = it.next(); -      // Headers.parseHeader() expects lowercase keys. -      String keyValue = entry.getKey() + ": " -          + entry.getValue()[HEADERS_MAP_INDEX_VALUE]; -      CharArrayBuffer buffer = new CharArrayBuffer(keyValue.length()); -      buffer.append(keyValue); -      // Parse it into the header container. -      cacheHeaders.parseHeader(buffer); -    } -    cacheResult = CacheManager.createCacheFile( -        url, responseCode, cacheHeaders, mimeType, true); -    if (cacheResult != null) { -      if (logEnabled) -        log("Saving into cache"); -      cacheResult.setEncoding(encoding); -      cacheResultUrl = url; -      return true; -    } else { -      log("Couldn't create cacheResult"); -      return false; -    } -  } - -  /** -   * Add data from the response body to the CacheResult created with -   * createCacheResult(). -   * @param data  A byte array of the next sequential bytes in the -   *              response body. -   * @param bytes The number of bytes to write from the start of -   *              the array. -   * @return True if all bytes successfully written, false on failure. -   */ -  public synchronized boolean appendCacheResult(byte[] data, int bytes) { -    if (cacheResult == null) { -      log("appendCacheResult() called without a CacheResult initialized"); -      return false; -    } -    try { -      cacheResult.getOutputStream().write(data, 0, bytes); -    } catch (IOException ex) { -      log("Got IOException writing cache data: " + ex); -      return false; -    } -    return true; -  } - -  /** -   * Save the completed CacheResult into the CacheManager. This must -   * have been created first with createCacheResult(). -   * @return Returns true if the entry has been successfully saved. -   */ -  public synchronized boolean saveCacheResult() { -    if (cacheResult == null || cacheResultUrl == null) { -      log("Tried to save cache result but createCacheResult not called"); -      return false; -    } -    if (logEnabled) -      log("Saving cache result"); -    CacheManager.saveCacheFile(cacheResultUrl, cacheResult); -    cacheResult = null; -    cacheResultUrl = null; -    return true; -  } - -  /** -   * Perform an HTTP request on the network. The underlying -   * HttpURLConnection is connected to the remote server and the -   * response headers are received. -   * @return True if the connection succeeded and headers have been -   *         received. False on connection failure. -   */ -  public boolean connectToRemote() { -    synchronized (this) { -      // Transfer a snapshot of our internally maintained map of request -      // headers to the connection object. -      applyRequestHeadersToConnection(); -      // Note blocking I/O so abort() can interrupt us. -      inBlockingOperation = true; -    } -    boolean success; -    try { -      if (logEnabled) -        log("Connecting to remote"); -      connection.connect(); -      if (logEnabled) -        log("Connected"); -      success = true; -    } catch (IOException e) { -      log("Got IOException in connect(): " + e.toString()); -      success = false; -    } finally { -      synchronized (this) { -        // No longer blocking. -        inBlockingOperation = false; -      } -    } -    return success; -  } - -  /** -   * Receive all headers from the server and populate -   * responseHeaders. This converts from the slightly odd format -   * returned by java.net.HttpURLConnection to a simpler -   * java.util.Map. -   * @return True if headers are successfully received, False on -   *         connection error. -   */ -  public synchronized boolean parseHeaders() { -    responseHeaders = new HashMap<String, String[]>(); -    /* HttpURLConnection contains a null terminated list of -     * key->value response pairs. If the key is null, then the value -     * contains the complete status line. If both key and value are -     * null for an index, we've reached the end. -     */ -    for (int i = 0; ; ++i) { -      String key = connection.getHeaderFieldKey(i); -      String value = connection.getHeaderField(i); -      if (logEnabled) -        log("header " + key + " -> " + value); -      if (key == null && value == null) { -        // End of list. -        break; -      } else if (key == null) { -        // The pair with null key has the complete status line in -        // the value, e.g "HTTP/1.0 200 OK". -        responseLine = value; -      } else if (value != null) { -        // If key and value are non-null, this is a response pair, e.g -        // "Content-Length" -> "5". Use setResponseHeader() to -        // correctly deal with case-insensitivity of the key. -        setResponseHeader(key, value); -      } else { -        // The key is non-null but value is null. Unexpected -        // condition. -        return false; -      } -    } -    return true; -  } - -  /** -   * Receive the next sequential bytes of the response body after -   * successful connection. This will receive up to the size of the -   * provided byte array. If there is no body, this will return 0 -   * bytes on the first call after connection. -   * @param  buf A pre-allocated byte array to receive data into. -   * @return The number of bytes from the start of the array which -   *         have been filled, 0 on EOF, or negative on error. -   */ -  public int receive(byte[] buf) { -    if (inputStream == null) { -      // If this is the first call, setup the InputStream. This may -      // fail if there were headers, but no body returned by the -      // server. -      try { -        inputStream = connection.getInputStream(); -      } catch (IOException inputException) { -        log("Failed to connect InputStream: " + inputException); -        // Not unexpected. For example, 404 response return headers, -        // and sometimes a body with a detailed error. Try the error -        // stream. -        inputStream = connection.getErrorStream(); -        if (inputStream == null) { -          // No error stream either. Treat as a 0 byte response. -          log("No InputStream"); -          return 0; // EOF. -        } -      } -    } -    synchronized (this) { -      // Note blocking I/O so abort() can interrupt us. -      inBlockingOperation = true; -    } -    int ret; -    try { -      int got = inputStream.read(buf); -      if (got > 0) { -        // Got some bytes, not EOF. -        ret = got; -      } else { -        // EOF. -        inputStream.close(); -        ret = 0; -      } -    } catch (IOException e) { -      // An abort() interrupts us by calling close() on our stream. -      log("Got IOException in inputStream.read(): " + e.toString()); -      ret = -1; -    } finally { -      synchronized (this) { -        // No longer blocking. -        inBlockingOperation = false; -      } -    } -    return ret; -  } - -  /** -   * For POST method requests, send a stream of data provided by the -   * native side in repeated callbacks. -   * @param data  A byte array containing the data to sent, or null -   *              if indicating EOF. -   * @param bytes The number of bytes from the start of the array to -   *              send, or 0 if indicating EOF. -   * @return True if all bytes were successfully sent, false on error. -   */ -  public boolean sendPostData(byte[] data, int bytes) { -    synchronized (this) { -      // Note blocking I/O so abort() can interrupt us. -      inBlockingOperation = true; -    } -    boolean success; -    try { -      OutputStream outputStream = connection.getOutputStream(); -      if (data == null && bytes == 0) { -        outputStream.close(); -      } else { -        outputStream.write(data, 0, bytes); -      } -      success = true; -    } catch (IOException e) { -      log("Got IOException in post: " + e.toString()); -      success = false; -    } finally { -      synchronized (this) { -        // No longer blocking. -        inBlockingOperation = false; -      } -    } -    return success; -  } -} diff --git a/core/java/android/webkit/gears/UrlInterceptHandlerGears.java b/core/java/android/webkit/gears/UrlInterceptHandlerGears.java index 288240e..2a5cbe9 100644 --- a/core/java/android/webkit/gears/UrlInterceptHandlerGears.java +++ b/core/java/android/webkit/gears/UrlInterceptHandlerGears.java @@ -64,11 +64,11 @@ public class UrlInterceptHandlerGears implements UrlInterceptHandler {    /** The unmodified (case-sensitive) key in the headers map is the     * same index as used by HttpRequestAndroid. */    public static final int HEADERS_MAP_INDEX_KEY = -      HttpRequestAndroid.HEADERS_MAP_INDEX_KEY; +      ApacheHttpRequestAndroid.HEADERS_MAP_INDEX_KEY;    /** The associated value in the headers map is the same index as     * used by HttpRequestAndroid. */    public static final int HEADERS_MAP_INDEX_VALUE = -      HttpRequestAndroid.HEADERS_MAP_INDEX_VALUE; +      ApacheHttpRequestAndroid.HEADERS_MAP_INDEX_VALUE;    /**     * Object passed to the native side, containing information about @@ -382,7 +382,7 @@ public class UrlInterceptHandlerGears implements UrlInterceptHandler {      // browser's cache for too long.      long now_ms = System.currentTimeMillis();      String expires = DateUtils.formatDate(new Date(now_ms + CACHE_EXPIRY_MS)); -    response.setResponseHeader(HttpRequestAndroid.KEY_EXPIRES, expires); +    response.setResponseHeader(ApacheHttpRequestAndroid.KEY_EXPIRES, expires);      // The browser is only interested in a small subset of headers,      // contained in a Headers object. Iterate the map of all headers      // and add them to Headers. | 
