| /* |
| * Copyright (C) 2007 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.net.http.EventHandler; |
| import android.net.http.Headers; |
| import android.os.Handler; |
| import android.os.Message; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| |
| |
| /** |
| * This abstract class is used for all content loaders that rely on streaming |
| * content into the rendering engine loading framework. |
| * |
| * The class implements a state machine to load the content into the frame in |
| * a similar manor to the way content arrives from the network. The class uses |
| * messages to move from one state to the next, which enables async. loading of |
| * the streamed content. |
| * |
| * Classes that inherit from this class must implement two methods, the first |
| * method is used to setup the InputStream and notify the loading framework if |
| * it can load it's content. The other method allows the derived class to add |
| * additional HTTP headers to the response. |
| * |
| * By default, content loaded with a StreamLoader is marked with a HTTP header |
| * that indicates the content should not be cached. |
| * |
| */ |
| abstract class StreamLoader extends Handler { |
| |
| public static final String NO_STORE = "no-store"; |
| |
| private static final int MSG_STATUS = 100; // Send status to loader |
| private static final int MSG_HEADERS = 101; // Send headers to loader |
| private static final int MSG_DATA = 102; // Send data to loader |
| private static final int MSG_END = 103; // Send endData to loader |
| |
| protected LoadListener mHandler; // loader class |
| protected InputStream mDataStream; // stream to read data from |
| protected long mContentLength; // content length of data |
| private byte [] mData; // buffer to pass data to loader with. |
| |
| /** |
| * Constructor. Although this class calls the LoadListener, it only calls |
| * the EventHandler Interface methods. LoadListener concrete class is used |
| * to avoid the penality of calling an interface. |
| * |
| * @param loadlistener The LoadListener to call with the data. |
| */ |
| StreamLoader(LoadListener loadlistener) { |
| mHandler = loadlistener; |
| } |
| |
| /** |
| * This method is called when the derived class should setup mDataStream, |
| * and call mHandler.status() to indicate that the load can occur. If it |
| * fails to setup, it should still call status() with the error code. |
| * |
| * @return true if stream was successfully setup |
| */ |
| protected abstract boolean setupStreamAndSendStatus(); |
| |
| /** |
| * This method is called when the headers are about to be sent to the |
| * load framework. The derived class has the opportunity to add addition |
| * headers. |
| * |
| * @param headers Map of HTTP headers that will be sent to the loader. |
| */ |
| abstract protected void buildHeaders(Headers headers); |
| |
| |
| /** |
| * Calling this method starts the load of the content for this StreamLoader. |
| * This method simply posts a message to send the status and returns |
| * immediately. |
| */ |
| public void load() { |
| if (!mHandler.isSynchronous()) { |
| sendMessage(obtainMessage(MSG_STATUS)); |
| } else { |
| // Load the stream synchronously. |
| if (setupStreamAndSendStatus()) { |
| // We were able to open the stream, create the array |
| // to pass data to the loader |
| mData = new byte[8192]; |
| sendHeaders(); |
| while (!sendData() && !mHandler.cancelled()); |
| closeStreamAndSendEndData(); |
| mHandler.loadSynchronousMessages(); |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see android.os.Handler#handleMessage(android.os.Message) |
| */ |
| public void handleMessage(Message msg) { |
| if (DebugFlags.STREAM_LOADER && mHandler.isSynchronous()) { |
| throw new AssertionError(); |
| } |
| if (mHandler.cancelled()) { |
| closeStreamAndSendEndData(); |
| return; |
| } |
| switch(msg.what) { |
| case MSG_STATUS: |
| if (setupStreamAndSendStatus()) { |
| // We were able to open the stream, create the array |
| // to pass data to the loader |
| mData = new byte[8192]; |
| sendMessage(obtainMessage(MSG_HEADERS)); |
| } |
| break; |
| case MSG_HEADERS: |
| sendHeaders(); |
| sendMessage(obtainMessage(MSG_DATA)); |
| break; |
| case MSG_DATA: |
| if (sendData()) { |
| sendMessage(obtainMessage(MSG_END)); |
| } else { |
| sendMessage(obtainMessage(MSG_DATA)); |
| } |
| break; |
| case MSG_END: |
| closeStreamAndSendEndData(); |
| break; |
| default: |
| super.handleMessage(msg); |
| break; |
| } |
| } |
| |
| /** |
| * Construct the headers and pass them to the EventHandler. |
| */ |
| private void sendHeaders() { |
| Headers headers = new Headers(); |
| if (mContentLength > 0) { |
| headers.setContentLength(mContentLength); |
| } |
| headers.setCacheControl(NO_STORE); |
| buildHeaders(headers); |
| mHandler.headers(headers); |
| } |
| |
| /** |
| * Read data from the stream and pass it to the EventHandler. |
| * If an error occurs reading the stream, then an error is sent to the |
| * EventHandler, and moves onto the next state - end of data. |
| * @return True if all the data has been read. False if sendData should be |
| * called again. |
| */ |
| private boolean sendData() { |
| if (mDataStream != null) { |
| try { |
| int amount = mDataStream.read(mData); |
| if (amount > 0) { |
| mHandler.data(mData, amount); |
| return false; |
| } |
| } catch (IOException ex) { |
| mHandler.error(EventHandler.FILE_ERROR, |
| ex.getMessage()); |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Close the stream and inform the EventHandler that load is complete. |
| */ |
| private void closeStreamAndSendEndData() { |
| if (mDataStream != null) { |
| try { |
| mDataStream.close(); |
| } catch (IOException ex) { |
| // ignore. |
| } |
| } |
| mHandler.endData(); |
| } |
| |
| } |