1 /*
2 * Copyright 2009, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #define LOG_TAG "webcore_test"
27 #include "config.h"
28
29 #include "Base64.h"
30 #include "CString.h"
31 #include "HTTPParsers.h"
32 #include "Intercept.h"
33 #include "ResourceHandle.h"
34 #include "ResourceHandleClient.h"
35 #include "ResourceRequest.h"
36 #include "ResourceResponse.h"
37 #include "StringHash.h"
38 #include "TextEncoding.h"
39 #include <utils/Log.h>
40 #include <wtf/HashMap.h>
41
create(ResourceHandle * handle,String url)42 PassRefPtr<WebCore::ResourceLoaderAndroid> MyResourceLoader::create(
43 ResourceHandle* handle, String url)
44 {
45 return adoptRef<WebCore::ResourceLoaderAndroid>(
46 new MyResourceLoader(handle, url));
47 }
48
handleRequest()49 void MyResourceLoader::handleRequest()
50 {
51 if (protocolIs(m_url, "data"))
52 loadData(m_url.substring(5)); // 5 for data:
53 else if (protocolIs(m_url, "file"))
54 loadFile(m_url.substring(7)); // 7 for file://
55 }
56
loadData(const String & data)57 void MyResourceLoader::loadData(const String& data)
58 {
59 LOGD("Loading data (%s) ...", data.latin1().data());
60 ResourceHandleClient* client = m_handle->client();
61 int index = data.find(',');
62 if (index == -1) {
63 client->cannotShowURL(m_handle);
64 return;
65 }
66
67 String mediaType = data.substring(0, index);
68 String base64 = data.substring(index + 1);
69
70 bool decode = mediaType.endsWith(";base64", false);
71 if (decode)
72 mediaType = mediaType.left(mediaType.length() - 7); // 7 for base64;
73
74 if (mediaType.isEmpty())
75 mediaType = "text/plain;charset=US-ASCII";
76
77 String mimeType = extractMIMETypeFromMediaType(mediaType);
78 String charset = extractCharsetFromMediaType(mediaType);
79
80 ResourceResponse response;
81 response.setMimeType(mimeType);
82
83 if (decode) {
84 base64 = decodeURLEscapeSequences(base64);
85 response.setTextEncodingName(charset);
86 client->didReceiveResponse(m_handle, response);
87
88 // FIXME: This is annoying. WebCore's Base64 decoder chokes on spaces.
89 // That is correct with strict decoding but html authors (particularly
90 // the acid3 authors) put spaces in the data which should be ignored.
91 // Remove them here before sending to the decoder.
92 Vector<char> in;
93 CString str = base64.latin1();
94 const char* chars = str.data();
95 unsigned i = 0;
96 while (i < str.length()) {
97 char c = chars[i];
98 // Don't send spaces or control characters.
99 if (c != ' ' && c != '\n' && c != '\t' && c != '\b'
100 && c != '\f' && c != '\r')
101 in.append(chars[i]);
102 i++;
103 }
104 Vector<char> out;
105 if (base64Decode(in, out) && out.size() > 0)
106 client->didReceiveData(m_handle, out.data(), out.size(), 0);
107 } else {
108 base64 = decodeURLEscapeSequences(base64, TextEncoding(charset));
109 response.setTextEncodingName("UTF-16");
110 client->didReceiveResponse(m_handle, response);
111 if (base64.length() > 0)
112 client->didReceiveData(m_handle, (const char*)base64.characters(),
113 base64.length() * sizeof(UChar), 0);
114 }
115 client->didFinishLoading(m_handle);
116 }
mimeTypeForExtension(const String & file)117 static String mimeTypeForExtension(const String& file)
118 {
119 static HashMap<String, String, CaseFoldingHash> extensionToMime;
120 if (extensionToMime.isEmpty()) {
121 extensionToMime.set("txt", "text/plain");
122 extensionToMime.set("html", "text/html");
123 extensionToMime.set("htm", "text/html");
124 extensionToMime.set("png", "image/png");
125 extensionToMime.set("jpeg", "image/jpeg");
126 extensionToMime.set("jpg", "image/jpeg");
127 extensionToMime.set("gif", "image/gif");
128 extensionToMime.set("ico", "image/x-icon");
129 extensionToMime.set("js", "text/javascript");
130 }
131 int dot = file.reverseFind('.');
132 String mime("text/plain");
133 if (dot != -1) {
134 String ext = file.substring(dot + 1);
135 if (extensionToMime.contains(ext))
136 mime = extensionToMime.get(ext);
137 }
138 return mime;
139 }
140
loadFile(const String & file)141 void MyResourceLoader::loadFile(const String& file)
142 {
143 LOGD("Loading file (%s) ...", file.latin1().data());
144 FILE* f = fopen(file.latin1().data(), "r");
145 ResourceHandleClient* client = m_handle->client();
146 if (!f) {
147 client->didFail(m_handle,
148 ResourceError("", -14, file, "Could not open file"));
149 } else {
150 ResourceResponse response;
151 response.setTextEncodingName("utf-8");
152 response.setMimeType(mimeTypeForExtension(file));
153 client->didReceiveResponse(m_handle, response);
154 char buf[512];
155 while (true) {
156 int res = fread(buf, 1, sizeof(buf), f);
157 if (res <= 0)
158 break;
159 client->didReceiveData(m_handle, buf, res, 0);
160 }
161 fclose(f);
162 client->didFinishLoading(m_handle);
163 }
164 }
165
startLoadingResource(ResourceHandle * handle,const ResourceRequest & req,bool ignore,bool ignore2)166 PassRefPtr<WebCore::ResourceLoaderAndroid> MyWebFrame::startLoadingResource(
167 ResourceHandle* handle, const ResourceRequest& req, bool ignore,
168 bool ignore2)
169 {
170 RefPtr<WebCore::ResourceLoaderAndroid> loader =
171 MyResourceLoader::create(handle, req.url().string());
172 m_requests.append(loader);
173 if (!m_timer.isActive())
174 m_timer.startOneShot(0);
175 return loader.release();
176 }
177
timerFired(Timer<MyWebFrame> *)178 void MyWebFrame::timerFired(Timer<MyWebFrame>*)
179 {
180 LOGD("Handling requests...");
181 Vector<RefPtr<WebCore::ResourceLoaderAndroid> > reqs;
182 reqs.swap(m_requests);
183 Vector<RefPtr<WebCore::ResourceLoaderAndroid> >::iterator i = reqs.begin();
184 Vector<RefPtr<WebCore::ResourceLoaderAndroid> >::iterator end = reqs.end();
185 for (; i != end; i++)
186 static_cast<MyResourceLoader*>((*i).get())->handleRequest();
187
188 LOGD("...done");
189 }
190