blob: 7d6d7cc5d3fc0625aab84c80a0093e89efc5df52 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
19import android.net.http.EventHandler;
20import android.net.http.Headers;
21import android.os.Handler;
22import android.os.Message;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023
24import java.io.IOException;
25import java.io.InputStream;
26
27
28/**
29 * This abstract class is used for all content loaders that rely on streaming
30 * content into the rendering engine loading framework.
31 *
32 * The class implements a state machine to load the content into the frame in
33 * a similar manor to the way content arrives from the network. The class uses
34 * messages to move from one state to the next, which enables async. loading of
35 * the streamed content.
36 *
37 * Classes that inherit from this class must implement two methods, the first
38 * method is used to setup the InputStream and notify the loading framework if
39 * it can load it's content. The other method allows the derived class to add
40 * additional HTTP headers to the response.
41 *
42 * By default, content loaded with a StreamLoader is marked with a HTTP header
43 * that indicates the content should not be cached.
44 *
45 */
46abstract class StreamLoader extends Handler {
47
48 public static final String NO_STORE = "no-store";
49
50 private static final int MSG_STATUS = 100; // Send status to loader
51 private static final int MSG_HEADERS = 101; // Send headers to loader
52 private static final int MSG_DATA = 102; // Send data to loader
53 private static final int MSG_END = 103; // Send endData to loader
54
55 protected LoadListener mHandler; // loader class
56 protected InputStream mDataStream; // stream to read data from
57 protected long mContentLength; // content length of data
58 private byte [] mData; // buffer to pass data to loader with.
59
60 /**
61 * Constructor. Although this class calls the LoadListener, it only calls
62 * the EventHandler Interface methods. LoadListener concrete class is used
63 * to avoid the penality of calling an interface.
64 *
65 * @param loadlistener The LoadListener to call with the data.
66 */
67 StreamLoader(LoadListener loadlistener) {
68 mHandler = loadlistener;
69 }
70
71 /**
72 * This method is called when the derived class should setup mDataStream,
73 * and call mHandler.status() to indicate that the load can occur. If it
74 * fails to setup, it should still call status() with the error code.
75 *
76 * @return true if stream was successfully setup
77 */
78 protected abstract boolean setupStreamAndSendStatus();
79
80 /**
81 * This method is called when the headers are about to be sent to the
82 * load framework. The derived class has the opportunity to add addition
83 * headers.
84 *
85 * @param headers Map of HTTP headers that will be sent to the loader.
86 */
87 abstract protected void buildHeaders(Headers headers);
88
89
90 /**
91 * Calling this method starts the load of the content for this StreamLoader.
92 * This method simply posts a message to send the status and returns
93 * immediately.
94 */
95 public void load() {
96 if (!mHandler.isSynchronous()) {
97 sendMessage(obtainMessage(MSG_STATUS));
98 } else {
99 // Load the stream synchronously.
100 if (setupStreamAndSendStatus()) {
101 // We were able to open the stream, create the array
102 // to pass data to the loader
103 mData = new byte[8192];
104 sendHeaders();
Patrick Scotte82dc422009-04-29 10:21:57 -0400105 while (!sendData() && !mHandler.cancelled());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 closeStreamAndSendEndData();
107 mHandler.loadSynchronousMessages();
108 }
109 }
110 }
111
112 /* (non-Javadoc)
113 * @see android.os.Handler#handleMessage(android.os.Message)
114 */
115 public void handleMessage(Message msg) {
Dave Bort42bc2ff2009-04-13 15:07:51 -0700116 if (WebView.DEBUG && mHandler.isSynchronous()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 throw new AssertionError();
118 }
Patrick Scotte82dc422009-04-29 10:21:57 -0400119 if (mHandler.cancelled()) {
120 closeStreamAndSendEndData();
121 return;
122 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 switch(msg.what) {
124 case MSG_STATUS:
125 if (setupStreamAndSendStatus()) {
126 // We were able to open the stream, create the array
127 // to pass data to the loader
128 mData = new byte[8192];
129 sendMessage(obtainMessage(MSG_HEADERS));
130 }
131 break;
132 case MSG_HEADERS:
133 sendHeaders();
134 sendMessage(obtainMessage(MSG_DATA));
135 break;
136 case MSG_DATA:
137 if (sendData()) {
138 sendMessage(obtainMessage(MSG_END));
139 } else {
140 sendMessage(obtainMessage(MSG_DATA));
141 }
142 break;
143 case MSG_END:
144 closeStreamAndSendEndData();
145 break;
146 default:
147 super.handleMessage(msg);
148 break;
149 }
150 }
151
152 /**
153 * Construct the headers and pass them to the EventHandler.
154 */
155 private void sendHeaders() {
156 Headers headers = new Headers();
157 if (mContentLength > 0) {
158 headers.setContentLength(mContentLength);
159 }
160 headers.setCacheControl(NO_STORE);
161 buildHeaders(headers);
162 mHandler.headers(headers);
163 }
164
165 /**
166 * Read data from the stream and pass it to the EventHandler.
167 * If an error occurs reading the stream, then an error is sent to the
168 * EventHandler, and moves onto the next state - end of data.
169 * @return True if all the data has been read. False if sendData should be
170 * called again.
171 */
172 private boolean sendData() {
173 if (mDataStream != null) {
174 try {
175 int amount = mDataStream.read(mData);
176 if (amount > 0) {
177 mHandler.data(mData, amount);
178 return false;
179 }
180 } catch (IOException ex) {
181 mHandler.error(EventHandler.FILE_ERROR,
182 ex.getMessage());
183 }
184 }
185 return true;
186 }
187
188 /**
189 * Close the stream and inform the EventHandler that load is complete.
190 */
191 private void closeStreamAndSendEndData() {
192 if (mDataStream != null) {
193 try {
194 mDataStream.close();
195 } catch (IOException ex) {
196 // ignore.
197 }
198 }
199 mHandler.endData();
200 }
201
202}