Merge change 9175

* changes:
  Fix 2016808. We used to send the new scale/scroll info to the UI thread after WebCore finish the first layout. As the picture in the UI thread is still the previous one, if there is a UI update happened before we generate the new picture, the new scale/scroll will be applied to the old picture.
This commit is contained in:
Android (Google) Code Review
2009-07-30 12:02:45 -07:00
2 changed files with 160 additions and 176 deletions

View File

@@ -206,11 +206,6 @@ public class WebView extends AbsoluteLayout
static final String LOGTAG = "webview";
static class ScaleLimitData {
int mMinScale;
int mMaxScale;
}
private static class ExtendedZoomControls extends FrameLayout {
public ExtendedZoomControls(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -472,7 +467,6 @@ public class WebView extends AbsoluteLayout
static final int UPDATE_TEXT_ENTRY_MSG_ID = 15;
static final int WEBCORE_INITIALIZED_MSG_ID = 16;
static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 17;
static final int DID_FIRST_LAYOUT_MSG_ID = 18;
static final int MOVE_OUT_OF_PLUGIN = 19;
static final int CLEAR_TEXT_ENTRY = 20;
@@ -502,7 +496,7 @@ public class WebView extends AbsoluteLayout
"UPDATE_TEXT_ENTRY_MSG_ID", // = 15;
"WEBCORE_INITIALIZED_MSG_ID", // = 16;
"UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 17;
"DID_FIRST_LAYOUT_MSG_ID", // = 18;
"18", // = 18;
"MOVE_OUT_OF_PLUGIN", // = 19;
"CLEAR_TEXT_ENTRY", // = 20;
"21", // = 21;
@@ -1892,6 +1886,13 @@ public class WebView extends AbsoluteLayout
r.bottom = viewToContent(r.bottom);
}
static class ViewSizeData {
int mWidth;
int mHeight;
int mTextWrapWidth;
float mScale;
}
/**
* 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)
@@ -1899,7 +1900,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
@@ -1914,8 +1916,16 @@ 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));
ViewSizeData data = new ViewSizeData();
data.mWidth = newWidth;
data.mHeight = newHeight;
// while in zoom overview mode, the text are wrapped to the screen
// width matching mLastScale. So that we don't trigger re-flow while
// toggling between overview mode and normal mode.
data.mTextWrapWidth = mInZoomOverview ? Math.round(viewWidth
/ mLastScale) : newWidth;
data.mScale = mActualScale;
mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
mLastWidthSent = newWidth;
mLastHeightSent = newHeight;
return true;
@@ -3877,11 +3887,6 @@ public class WebView extends AbsoluteLayout
if (!mDragFromTextInput) {
nativeHideCursor();
}
// 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() && !mInZoomOverview
&& settings.getBuiltInZoomControls()
@@ -4982,15 +4987,35 @@ public class WebView extends AbsoluteLayout
final WebViewCore.DrawData draw =
(WebViewCore.DrawData) msg.obj;
final Point viewSize = draw.mViewPoint;
if (mZoomScale > 0) {
// use the same logic in sendViewSizeZoom() to make sure
// the mZoomScale has matched the viewSize so that we
// can clear mZoomScale
if (Math.round(viewWidth / mZoomScale) == viewSize.x) {
mZoomScale = 0;
mWebViewCore.sendMessage(EventHub.SET_SNAP_ANCHOR,
0, 0);
boolean useWideViewport =
mWebViewCore.getSettings().getUseWideViewPort();
WebViewCore.RestoreState restoreState = draw.mRestoreState;
if (restoreState != null) {
mInZoomOverview = false;
mLastScale = restoreState.mTextWrapScale;
if (restoreState.mMinScale == 0) {
mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
mMinZoomScaleFixed = false;
} else {
mMinZoomScale = restoreState.mMinScale;
mMinZoomScaleFixed = true;
}
if (restoreState.mMaxScale == 0) {
mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
} else {
mMaxZoomScale = restoreState.mMaxScale;
}
if (useWideViewport && restoreState.mViewScale == 0) {
mInZoomOverview = ENABLE_DOUBLETAP_ZOOM;
}
setNewZoomScale(mLastScale, false);
setContentScrollTo(restoreState.mScrollX,
restoreState.mScrollY);
// As we are on a new page, remove the WebTextView. This
// is necessary for page loads driven by webkit, and in
// particular when the user was on a password field, so
// the WebTextView was visible.
clearTextEntry();
}
// We update the layout (i.e. request a layout from the
// view system) if the last view size that we sent to
@@ -5009,7 +5034,7 @@ public class WebView extends AbsoluteLayout
if (mPictureListener != null) {
mPictureListener.onNewPicture(WebView.this, capturePicture());
}
if (mWebViewCore.getSettings().getUseWideViewPort()) {
if (useWideViewport) {
mZoomOverviewWidth = Math.max(draw.mMinPrefWidth,
draw.mViewPoint.x);
}
@@ -5056,76 +5081,6 @@ public class WebView extends AbsoluteLayout
}
}
break;
case DID_FIRST_LAYOUT_MSG_ID: {
if (mNativeClass == 0) {
break;
}
ScaleLimitData scaleLimit = (ScaleLimitData) msg.obj;
int minScale = scaleLimit.mMinScale;
if (minScale == 0) {
mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
mMinZoomScaleFixed = false;
} else {
mMinZoomScale = (float) (minScale / 100.0);
mMinZoomScaleFixed = true;
}
int maxScale = scaleLimit.mMaxScale;
if (maxScale == 0) {
mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
} else {
mMaxZoomScale = (float) (maxScale / 100.0);
}
// If history Picture is drawn, don't update zoomWidth
if (mDrawHistory) {
break;
}
int width = getViewWidth();
if (width == 0) {
break;
}
final WebSettings settings = mWebViewCore.getSettings();
int initialScale = msg.arg1;
int viewportWidth = msg.arg2;
// start a new page with DEFAULT_SCALE zoom scale.
float scale = mDefaultScale;
mInZoomOverview = false;
if (mInitialScale > 0) {
scale = mInitialScale / 100.0f;
} else {
if (initialScale == -1) break;
if (settings.getUseWideViewPort()) {
// force viewSizeChanged by setting mLastWidthSent
// to 0
mLastWidthSent = 0;
}
if (initialScale == 0) {
// if viewportWidth is defined and it is smaller
// than the view width, zoom in to fill the view
if (viewportWidth > 0 && viewportWidth < width) {
scale = (float) width / viewportWidth;
} else {
if (settings.getUseWideViewPort()) {
mInZoomOverview = ENABLE_DOUBLETAP_ZOOM;
}
}
} else if (initialScale < 0) {
// this should only happen when
// ENABLE_DOUBLETAP_ZOOM is true
mInZoomOverview = true;
scale = -initialScale / 100.0f;
} else {
scale = initialScale / 100.0f;
}
}
mLastScale = scale;
setNewZoomScale(scale, false);
// As we are on a new page, remove the WebTextView. This
// is necessary for page loads driven by webkit, and in
// particular when the user was on a password field, so
// the WebTextView was visible.
clearTextEntry();
break;
}
case MOVE_OUT_OF_PLUGIN:
if (nativePluginEatsNavKey()) {
navHandledKey(msg.arg1, 1, false, 0, true);

View File

@@ -443,10 +443,6 @@ final class WebViewCore {
private native void nativeUpdateFrameCache();
private native void nativeSetSnapAnchor(int x, int y);
private native void nativeSnapToAnchor();
private native void nativeSetBackgroundColor(int color);
private native void nativeDumpDomTree(boolean useFile);
@@ -674,7 +670,7 @@ final class WebViewCore {
"CLICK", // = 118;
"SET_NETWORK_STATE", // = 119;
"DOC_HAS_IMAGES", // = 120;
"SET_SNAP_ANCHOR", // = 121;
"121", // = 121;
"DELETE_SELECTION", // = 122;
"LISTBOX_CHOICES", // = 123;
"SINGLE_LISTBOX_CHOICE", // = 124;
@@ -725,7 +721,6 @@ final class WebViewCore {
static final int CLICK = 118;
static final int SET_NETWORK_STATE = 119;
static final int DOC_HAS_IMAGES = 120;
static final int SET_SNAP_ANCHOR = 121;
static final int DELETE_SELECTION = 122;
static final int LISTBOX_CHOICES = 123;
static final int SINGLE_LISTBOX_CHOICE = 124;
@@ -904,11 +899,13 @@ final class WebViewCore {
nativeClick(msg.arg1, msg.arg2);
break;
case VIEW_SIZE_CHANGED:
viewSizeChanged(msg.arg1, msg.arg2,
((Float) msg.obj).floatValue());
case VIEW_SIZE_CHANGED: {
WebView.ViewSizeData data =
(WebView.ViewSizeData) msg.obj;
viewSizeChanged(data.mWidth, data.mHeight,
data.mTextWrapWidth, data.mScale);
break;
}
case SET_SCROLL_OFFSET:
// note: these are in document coordinates
// (inv-zoom)
@@ -1107,10 +1104,6 @@ final class WebViewCore {
imageResult.sendToTarget();
break;
case SET_SNAP_ANCHOR:
nativeSetSnapAnchor(msg.arg1, msg.arg2);
break;
case DELETE_SELECTION:
DeleteSelectionData deleteSelectionData
= (DeleteSelectionData) msg.obj;
@@ -1401,16 +1394,20 @@ final class WebViewCore {
// These values are used to avoid requesting a layout based on old values
private int mCurrentViewWidth = 0;
private int mCurrentViewHeight = 0;
private float mCurrentViewScale = 1.0f;
// notify webkit that our virtual view size changed size (after inv-zoom)
private void viewSizeChanged(int w, int h, float scale) {
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE onSizeChanged");
private void viewSizeChanged(int w, int h, int textwrapWidth, float scale) {
if (DebugFlags.WEB_VIEW_CORE) {
Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h
+ "; textwrapWidth=" + textwrapWidth + "; scale=" + scale);
}
if (w == 0) {
Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
return;
}
int width = w;
if (mSettings.getUseWideViewPort()) {
int width;
if (mViewportWidth == -1) {
if (mSettings.getLayoutAlgorithm() ==
WebSettings.LayoutAlgorithm.NORMAL) {
@@ -1433,19 +1430,14 @@ final class WebViewCore {
} else {
width = Math.max(w, mViewportWidth);
}
// while in zoom overview mode, the text are wrapped to the screen
// width matching mWebView.mLastScale. So that we don't trigger
// re-flow while toggling between overview mode and normal mode.
nativeSetSize(width, Math.round((float) width * h / w),
Math.round(mWebView.mInZoomOverview ? w * scale
/ mWebView.mLastScale : w), scale, w, h);
} else {
nativeSetSize(w, h, w, scale, w, h);
}
nativeSetSize(width, width == w ? h : Math.round((float) width * h / w),
textwrapWidth, scale, w, h);
// Remember the current width and height
boolean needInvalidate = (mCurrentViewWidth == 0);
mCurrentViewWidth = w;
mCurrentViewHeight = h;
mCurrentViewScale = scale;
if (needInvalidate) {
// ensure {@link #webkitDraw} is called as we were blocking in
// {@link #contentDraw} when mCurrentViewWidth is 0
@@ -1490,15 +1482,30 @@ final class WebViewCore {
// Used to end scale+scroll mode, accessed by both threads
boolean mEndScaleZoom = false;
public class DrawData {
public DrawData() {
// mRestoreState is set in didFirstLayout(), and reset in the next
// webkitDraw after passing it to the UI thread.
private RestoreState mRestoreState = null;
static class RestoreState {
float mMinScale;
float mMaxScale;
float mViewScale;
float mTextWrapScale;
int mScrollX;
int mScrollY;
}
static class DrawData {
DrawData() {
mInvalRegion = new Region();
mWidthHeight = new Point();
}
public Region mInvalRegion;
public Point mViewPoint;
public Point mWidthHeight;
public int mMinPrefWidth;
Region mInvalRegion;
Point mViewPoint;
Point mWidthHeight;
int mMinPrefWidth;
RestoreState mRestoreState; // only non-null if it is for the first
// picture set after the first layout
}
private void webkitDraw() {
@@ -1517,6 +1524,10 @@ final class WebViewCore {
if (WebView.ENABLE_DOUBLETAP_ZOOM && mSettings.getUseWideViewPort()) {
draw.mMinPrefWidth = nativeGetContentMinPrefWidth();
}
if (mRestoreState != null) {
draw.mRestoreState = mRestoreState;
mRestoreState = null;
}
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
Message.obtain(mWebView.mPrivateHandler,
WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
@@ -1527,9 +1538,6 @@ final class WebViewCore {
mWebkitScrollY).sendToTarget();
mWebkitScrollX = mWebkitScrollY = 0;
}
// nativeSnapToAnchor() needs to be called after NEW_PICTURE_MSG_ID
// is sent, so that scroll will be based on the new content size.
nativeSnapToAnchor();
}
}
@@ -1751,17 +1759,8 @@ final class WebViewCore {
// called by JNI
private void didFirstLayout(boolean standardLoad) {
// Trick to ensure that the Picture has the exact height for the content
// by forcing to layout with 0 height after the page is ready, which is
// indicated by didFirstLayout. This is essential to get rid of the
// white space in the GMail which uses WebView for message view.
if (mWebView != null && mWebView.mHeightCanMeasure) {
mWebView.mLastHeightSent = 0;
// Send a negative scale to indicate that WebCore should reuse the
// current scale
mEventHub.sendMessage(Message.obtain(null,
EventHub.VIEW_SIZE_CHANGED, mWebView.mLastWidthSent,
mWebView.mLastHeightSent, -1.0f));
if (DebugFlags.WEB_VIEW_CORE) {
Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad);
}
mBrowserFrame.didFirstLayout();
@@ -1769,6 +1768,9 @@ final class WebViewCore {
// reset the scroll position as it is a new page now
mWebkitScrollX = mWebkitScrollY = 0;
// for non-standard load, we only adjust scale if mRestoredScale > 0
if (mWebView == null || (mRestoredScale == 0 && !standardLoad)) return;
// set the viewport settings from WebKit
setViewportSettingsFromNative();
@@ -1820,47 +1822,74 @@ final class WebViewCore {
}
// now notify webview
if (mWebView != null) {
WebView.ScaleLimitData scaleLimit = new WebView.ScaleLimitData();
scaleLimit.mMinScale = mViewportMinimumScale;
scaleLimit.mMaxScale = mViewportMaximumScale;
if (mRestoredScale > 0) {
Message.obtain(mWebView.mPrivateHandler,
WebView.DID_FIRST_LAYOUT_MSG_ID,
mRestoredScreenWidthScale > 0 ?
-mRestoredScreenWidthScale : mRestoredScale, 0,
scaleLimit).sendToTarget();
int webViewWidth = Math.round(mCurrentViewWidth * mCurrentViewScale);
mRestoreState = new RestoreState();
mRestoreState.mMinScale = mViewportMinimumScale / 100.0f;
mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f;
mRestoreState.mScrollX = mRestoredX;
mRestoreState.mScrollY = mRestoredY;
if (mRestoredScale > 0) {
if (mRestoredScreenWidthScale > 0) {
mRestoreState.mTextWrapScale =
mRestoredScreenWidthScale / 100.0f;
// 0 will trigger WebView to turn on zoom overview mode
mRestoreState.mViewScale = 0;
} else {
// if standardLoad is true, use mViewportInitialScale, otherwise
// pass -1 to the WebView to indicate no change of the scale.
Message.obtain(mWebView.mPrivateHandler,
WebView.DID_FIRST_LAYOUT_MSG_ID,
standardLoad ? mViewportInitialScale : -1,
mViewportWidth, scaleLimit).sendToTarget();
mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
mRestoredScale / 100.0f;
}
// force an early draw for quick feedback after the first layout
if (mCurrentViewWidth != 0) {
synchronized (this) {
if (mDrawIsScheduled) {
mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
}
mDrawIsScheduled = true;
// if no restored offset, move the new page to (0, 0)
mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
EventHub.MESSAGE_RELAY, Message.obtain(
mWebView.mPrivateHandler,
WebView.SCROLL_TO_MSG_ID, mRestoredX,
mRestoredY)));
mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
EventHub.WEBKIT_DRAW));
}
} else {
if (mViewportInitialScale > 0) {
mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
mViewportInitialScale / 100.0f;
} else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) {
mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
(float) webViewWidth / mViewportWidth;
} else {
mRestoreState.mTextWrapScale =
WebView.DEFAULT_SCALE_PERCENT / 100.0f;
// 0 will trigger WebView to turn on zoom overview mode
mRestoreState.mViewScale = 0;
}
// reset restored offset, scale
mRestoredX = mRestoredY = mRestoredScale = mRestoredScreenWidthScale = 0;
}
if (mWebView.mHeightCanMeasure) {
// Trick to ensure that the Picture has the exact height for the
// content by forcing to layout with 0 height after the page is
// ready, which is indicated by didFirstLayout. This is essential to
// get rid of the white space in the GMail which uses WebView for
// message view.
mWebView.mLastHeightSent = 0;
// Send a negative scale to indicate that WebCore should reuse
// the current scale
WebView.ViewSizeData data = new WebView.ViewSizeData();
data.mWidth = mWebView.mLastWidthSent;
data.mHeight = 0;
// if mHeightCanMeasure is true, getUseWideViewPort() can't be
// true. It is safe to use mWidth for mTextWrapWidth.
data.mTextWrapWidth = data.mWidth;
data.mScale = -1.0f;
mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
EventHub.VIEW_SIZE_CHANGED, data));
} else if (mSettings.getUseWideViewPort() && mCurrentViewWidth > 0) {
WebView.ViewSizeData data = new WebView.ViewSizeData();
// mViewScale as 0 means it is in zoom overview mode. So we don't
// know the exact scale. If mRestoredScale is non-zero, use it;
// otherwise just use mTextWrapScale as the initial scale.
data.mScale = mRestoreState.mViewScale == 0
? (mRestoredScale > 0 ? mRestoredScale
: mRestoreState.mTextWrapScale)
: mRestoreState.mViewScale;
data.mWidth = Math.round(webViewWidth / data.mScale);
data.mHeight = mCurrentViewHeight * data.mWidth
/ mCurrentViewWidth;
data.mTextWrapWidth = Math.round(webViewWidth
/ mRestoreState.mTextWrapScale);
mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
EventHub.VIEW_SIZE_CHANGED, data));
}
// reset restored offset, scale
mRestoredX = mRestoredY = mRestoredScale = mRestoredScreenWidthScale = 0;
}
// called by JNI