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 "HTTPParsers.h" 31 #include "Intercept.h" 32 #include "ResourceHandle.h" 33 #include "ResourceHandleClient.h" 34 #include "ResourceRequest.h" 35 #include "ResourceResponse.h" 36 #include "TextEncoding.h" 37 38 #include <utils/Log.h> 39 #include <wtf/HashMap.h> 40 #include <wtf/text/CString.h> 41 #include <wtf/text/StringHash.h> 42 43 PassRefPtr<WebCore::ResourceLoaderAndroid> MyResourceLoader::create( 44 ResourceHandle* handle, String url) 45 { 46 return adoptRef<WebCore::ResourceLoaderAndroid>( 47 new MyResourceLoader(handle, url)); 48 } 49 50 void MyResourceLoader::handleRequest() 51 { 52 if (protocolIs(m_url, "data")) 53 loadData(m_url.substring(5)); // 5 for data: 54 else if (protocolIs(m_url, "file")) 55 loadFile(m_url.substring(7)); // 7 for file:// 56 } 57 58 void MyResourceLoader::loadData(const String& data) 59 { 60 LOGD("Loading data (%s) ...", data.latin1().data()); 61 ResourceHandleClient* client = m_handle->client(); 62 int index = data.find(','); 63 if (index == -1) { 64 client->cannotShowURL(m_handle); 65 return; 66 } 67 68 String mediaType = data.substring(0, index); 69 String base64 = data.substring(index + 1); 70 71 bool decode = mediaType.endsWith(";base64", false); 72 if (decode) 73 mediaType = mediaType.left(mediaType.length() - 7); // 7 for base64; 74 75 if (mediaType.isEmpty()) 76 mediaType = "text/plain;charset=US-ASCII"; 77 78 String mimeType = extractMIMETypeFromMediaType(mediaType); 79 String charset = extractCharsetFromMediaType(mediaType); 80 81 ResourceResponse response; 82 response.setMimeType(mimeType); 83 84 if (decode) { 85 base64 = decodeURLEscapeSequences(base64); 86 response.setTextEncodingName(charset); 87 client->didReceiveResponse(m_handle, response); 88 89 // FIXME: This is annoying. WebCore's Base64 decoder chokes on spaces. 90 // That is correct with strict decoding but html authors (particularly 91 // the acid3 authors) put spaces in the data which should be ignored. 92 // Remove them here before sending to the decoder. 93 Vector<char> in; 94 CString str = base64.latin1(); 95 const char* chars = str.data(); 96 unsigned i = 0; 97 while (i < str.length()) { 98 char c = chars[i]; 99 // Don't send spaces or control characters. 100 if (c != ' ' && c != '\n' && c != '\t' && c != '\b' 101 && c != '\f' && c != '\r') 102 in.append(chars[i]); 103 i++; 104 } 105 Vector<char> out; 106 if (base64Decode(in, out) && out.size() > 0) 107 client->didReceiveData(m_handle, out.data(), out.size(), 0); 108 } else { 109 base64 = decodeURLEscapeSequences(base64, TextEncoding(charset)); 110 response.setTextEncodingName("UTF-16"); 111 client->didReceiveResponse(m_handle, response); 112 if (base64.length() > 0) 113 client->didReceiveData(m_handle, (const char*)base64.characters(), 114 base64.length() * sizeof(UChar), 0); 115 } 116 client->didFinishLoading(m_handle, 0); 117 } 118 static String mimeTypeForExtension(const String& file) 119 { 120 static HashMap<String, String, CaseFoldingHash> extensionToMime; 121 if (extensionToMime.isEmpty()) { 122 extensionToMime.set("txt", "text/plain"); 123 extensionToMime.set("html", "text/html"); 124 extensionToMime.set("htm", "text/html"); 125 extensionToMime.set("png", "image/png"); 126 extensionToMime.set("jpeg", "image/jpeg"); 127 extensionToMime.set("jpg", "image/jpeg"); 128 extensionToMime.set("gif", "image/gif"); 129 extensionToMime.set("ico", "image/x-icon"); 130 extensionToMime.set("js", "text/javascript"); 131 } 132 int dot = file.reverseFind('.'); 133 String mime("text/plain"); 134 if (dot != -1) { 135 String ext = file.substring(dot + 1); 136 if (extensionToMime.contains(ext)) 137 mime = extensionToMime.get(ext); 138 } 139 return mime; 140 } 141 142 void MyResourceLoader::loadFile(const String& file) 143 { 144 LOGD("Loading file (%s) ...", file.latin1().data()); 145 FILE* f = fopen(file.latin1().data(), "r"); 146 ResourceHandleClient* client = m_handle->client(); 147 if (!f) { 148 client->didFail(m_handle, 149 ResourceError("", -14, file, "Could not open file")); 150 } else { 151 ResourceResponse response; 152 response.setTextEncodingName("utf-8"); 153 response.setMimeType(mimeTypeForExtension(file)); 154 client->didReceiveResponse(m_handle, response); 155 char buf[512]; 156 while (true) { 157 int res = fread(buf, 1, sizeof(buf), f); 158 if (res <= 0) 159 break; 160 client->didReceiveData(m_handle, buf, res, 0); 161 } 162 fclose(f); 163 client->didFinishLoading(m_handle, 0); 164 } 165 } 166 167 PassRefPtr<WebCore::ResourceLoaderAndroid> MyWebFrame::startLoadingResource( 168 ResourceHandle* handle, const ResourceRequest& req, bool ignore, 169 bool ignore2) 170 { 171 RefPtr<WebCore::ResourceLoaderAndroid> loader = 172 MyResourceLoader::create(handle, req.url().string()); 173 m_requests.append(loader); 174 if (!m_timer.isActive()) 175 m_timer.startOneShot(0); 176 return loader.release(); 177 } 178 179 void MyWebFrame::timerFired(Timer<MyWebFrame>*) 180 { 181 LOGD("Handling requests..."); 182 Vector<RefPtr<WebCore::ResourceLoaderAndroid> > reqs; 183 reqs.swap(m_requests); 184 Vector<RefPtr<WebCore::ResourceLoaderAndroid> >::iterator i = reqs.begin(); 185 Vector<RefPtr<WebCore::ResourceLoaderAndroid> >::iterator end = reqs.end(); 186 for (; i != end; i++) 187 static_cast<MyResourceLoader*>((*i).get())->handleRequest(); 188 189 LOGD("...done"); 190 } 191