Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 816c0def authored by John Reck's avatar John Reck
Browse files

Initial support for view state serialization

 Framework for serializing the view state
 Still needs to prevent sending messages to webkit (pinch
 zoom doesn't work correctly as a result)

Change-Id: Ic3f8fe19b27ff1f841b556e87f582dab2a6d903b
parent 2032eee0
Loading
Loading
Loading
Loading
+80 −0
Original line number Diff line number Diff line
/*
 * 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() {}
}
+110 −61
Original line number Diff line number Diff line
@@ -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:
@@ -8345,6 +8330,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.
@@ -9047,6 +9095,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);