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 42 PassRefPtr<WebCore::ResourceLoaderAndroid> MyResourceLoader::create( 43 ResourceHandle* handle, String url) 44 { 45 return adoptRef<WebCore::ResourceLoaderAndroid>( 46 new MyResourceLoader(handle, url)); 47 } 48 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 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 } 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 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 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 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