Merge "Initial support for view state serialization"

This commit is contained in:
John Reck
2011-06-03 15:21:16 -07:00
committed by Android (Google) Code Review
2 changed files with 190 additions and 61 deletions

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.webkit;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.webkit.WebViewCore.DrawData;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @hide
*/
class ViewStateSerializer {
private static final int WORKING_STREAM_STORAGE = 16 * 1024;
static final int VERSION = 1;
static boolean serializeViewState(OutputStream stream, WebView web)
throws IOException {
DataOutputStream dos = new DataOutputStream(stream);
dos.writeInt(VERSION);
dos.writeInt(web.getContentWidth());
dos.writeInt(web.getContentHeight());
return nativeSerializeViewState(web.getBaseLayer(), dos,
new byte[WORKING_STREAM_STORAGE]);
}
static DrawData deserializeViewState(InputStream stream, WebView web)
throws IOException {
DataInputStream dis = new DataInputStream(stream);
int version = dis.readInt();
if (version != VERSION) {
throw new IOException("Unexpected version: " + version);
}
int contentWidth = dis.readInt();
int contentHeight = dis.readInt();
int baseLayer = nativeDeserializeViewState(dis,
new byte[WORKING_STREAM_STORAGE]);
final WebViewCore.DrawData draw = new WebViewCore.DrawData();
draw.mViewState = new WebViewCore.ViewState();
int viewWidth = web.getViewWidth();
int viewHeight = web.getViewHeightWithTitle() - web.getTitleHeight();
draw.mViewSize = new Point(viewWidth, viewHeight);
draw.mContentSize = new Point(contentWidth, contentHeight);
draw.mViewState.mDefaultScale = web.getDefaultZoomScale();
draw.mBaseLayer = baseLayer;
draw.mInvalRegion = new Region(0, 0, contentWidth, contentHeight);
return draw;
}
private static native boolean nativeSerializeViewState(int baseLayer,
OutputStream stream, byte[] storage);
// Returns a pointer to the BaseLayer
private static native int nativeDeserializeViewState(
InputStream stream, byte[] storage);
private ViewStateSerializer() {}
}

View File

@@ -84,6 +84,7 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.webkit.WebTextView.AutoCompleteAdapter;
import android.webkit.WebViewCore.DrawData;
import android.webkit.WebViewCore.EventHub;
import android.webkit.WebViewCore.TouchEventData;
import android.webkit.WebViewCore.TouchHighlightData;
@@ -102,6 +103,9 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
@@ -1400,7 +1404,7 @@ public class WebView extends AbsoluteLayout
return getViewHeightWithTitle() - getVisibleTitleHeight();
}
private int getViewHeightWithTitle() {
int getViewHeightWithTitle() {
int height = getHeight();
if (isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
height -= getHorizontalScrollbarHeight();
@@ -1785,6 +1789,42 @@ public class WebView extends AbsoluteLayout
return true;
}
/**
* Saves the view data to the output stream. The output is highly
* version specific, and may not be able to be loaded by newer versions
* of WebView.
* @param stream The {@link OutputStream} to save to
* @return True if saved successfully
* @hide
*/
public boolean saveViewState(OutputStream stream) {
try {
return ViewStateSerializer.serializeViewState(stream, this);
} catch (IOException e) {
Log.w(LOGTAG, "Failed to saveViewState", e);
}
return false;
}
/**
* Loads the view data from the input stream. See
* {@link #saveViewState(OutputStream)} for more information.
* @param stream The {@link InputStream} to load from
* @return True if loaded successfully
* @hide
*/
public boolean loadViewState(InputStream stream) {
try {
mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
DrawData draw = ViewStateSerializer.deserializeViewState(stream, this);
setNewPicture(draw);
return true;
} catch (IOException e) {
Log.w(LOGTAG, "Failed to loadViewState", e);
}
return false;
}
/**
* Restore the state of this WebView from the given map used in
* {@link android.app.Activity#onRestoreInstanceState}. This method should
@@ -4192,6 +4232,10 @@ public class WebView extends AbsoluteLayout
}
}
int getBaseLayer() {
return nativeGetBaseLayer();
}
private void onZoomAnimationStart() {
// If it is in password mode, turn it off so it does not draw misplaced.
if (inEditingMode() && nativeFocusCandidateIsPassword()) {
@@ -7966,66 +8010,7 @@ public class WebView extends AbsoluteLayout
case NEW_PICTURE_MSG_ID: {
// called for new content
final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj;
WebViewCore.ViewState viewState = draw.mViewState;
boolean isPictureAfterFirstLayout = viewState != null;
setBaseLayer(draw.mBaseLayer, draw.mInvalRegion,
getSettings().getShowVisualIndicator(),
isPictureAfterFirstLayout);
final Point viewSize = draw.mViewSize;
if (isPictureAfterFirstLayout) {
// Reset the last sent data here since dealing with new page.
mLastWidthSent = 0;
mZoomManager.onFirstLayout(draw);
if (!mDrawHistory) {
// Do not send the scroll event for this particular
// scroll message. Note that a scroll event may
// still be fired if the user scrolls before the
// message can be handled.
mSendScrollEvent = false;
setContentScrollTo(viewState.mScrollX, viewState.mScrollY);
mSendScrollEvent = true;
// 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
// WebCore matches the view size of the picture we just
// received in the fixed dimension.
final boolean updateLayout = viewSize.x == mLastWidthSent
&& viewSize.y == mLastHeightSent;
// Don't send scroll event for picture coming from webkit,
// since the new picture may cause a scroll event to override
// the saved history scroll position.
mSendScrollEvent = false;
recordNewContentSize(draw.mContentSize.x,
draw.mContentSize.y, updateLayout);
mSendScrollEvent = true;
if (DebugFlags.WEB_VIEW) {
Rect b = draw.mInvalRegion.getBounds();
Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
b.left+","+b.top+","+b.right+","+b.bottom+"}");
}
invalidateContentRect(draw.mInvalRegion.getBounds());
if (mPictureListener != null) {
mPictureListener.onNewPicture(WebView.this, capturePicture());
}
// update the zoom information based on the new picture
mZoomManager.onNewPicture(draw);
if (draw.mFocusSizeChanged && inEditingMode()) {
mFocusSizeChanged = true;
}
if (isPictureAfterFirstLayout) {
mViewManager.postReadyToDrawAll();
}
setNewPicture(draw);
break;
}
case WEBCORE_INITIALIZED_MSG_ID:
@@ -8344,6 +8329,69 @@ public class WebView extends AbsoluteLayout
}
}
void setNewPicture(final WebViewCore.DrawData draw) {
WebViewCore.ViewState viewState = draw.mViewState;
boolean isPictureAfterFirstLayout = viewState != null;
setBaseLayer(draw.mBaseLayer, draw.mInvalRegion,
getSettings().getShowVisualIndicator(),
isPictureAfterFirstLayout);
final Point viewSize = draw.mViewSize;
if (isPictureAfterFirstLayout) {
// Reset the last sent data here since dealing with new page.
mLastWidthSent = 0;
mZoomManager.onFirstLayout(draw);
if (!mDrawHistory) {
// Do not send the scroll event for this particular
// scroll message. Note that a scroll event may
// still be fired if the user scrolls before the
// message can be handled.
mSendScrollEvent = false;
setContentScrollTo(viewState.mScrollX, viewState.mScrollY);
mSendScrollEvent = true;
// 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
// WebCore matches the view size of the picture we just
// received in the fixed dimension.
final boolean updateLayout = viewSize.x == mLastWidthSent
&& viewSize.y == mLastHeightSent;
// Don't send scroll event for picture coming from webkit,
// since the new picture may cause a scroll event to override
// the saved history scroll position.
mSendScrollEvent = false;
recordNewContentSize(draw.mContentSize.x,
draw.mContentSize.y, updateLayout);
mSendScrollEvent = true;
if (DebugFlags.WEB_VIEW) {
Rect b = draw.mInvalRegion.getBounds();
Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
b.left+","+b.top+","+b.right+","+b.bottom+"}");
}
invalidateContentRect(draw.mInvalRegion.getBounds());
if (mPictureListener != null) {
mPictureListener.onNewPicture(WebView.this, capturePicture());
}
// update the zoom information based on the new picture
mZoomManager.onNewPicture(draw);
if (draw.mFocusSizeChanged && inEditingMode()) {
mFocusSizeChanged = true;
}
if (isPictureAfterFirstLayout) {
mViewManager.postReadyToDrawAll();
}
}
/**
* Used when receiving messages for REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID
* and UPDATE_TEXT_SELECTION_MSG_ID. Update the selection of WebTextView.
@@ -9046,6 +9094,7 @@ public class WebView extends AbsoluteLayout
private native void nativeSetHeightCanMeasure(boolean measure);
private native void nativeSetBaseLayer(int layer, Region invalRegion,
boolean showVisualIndicator, boolean isPictureAfterFirstLayout);
private native int nativeGetBaseLayer();
private native void nativeShowCursorTimed();
private native void nativeReplaceBaseContent(int content);
private native void nativeCopyBaseContentToPicture(Picture pict);