1 /* 2 * Copyright 2010, 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 THE COPYRIGHT OWNER 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 #include "config.h" 27 #include "WebCache.h" 28 29 #include "JNIUtility.h" 30 #include "WebCoreJni.h" 31 #include "WebRequestContext.h" 32 #include "WebUrlLoaderClient.h" 33 #include "net/http/http_network_session.h" 34 #include <wtf/text/CString.h> 35 36 using namespace WTF; 37 using namespace disk_cache; 38 using namespace net; 39 using namespace std; 40 41 namespace android { 42 43 static WTF::Mutex instanceMutex; 44 45 static string storageDirectory() 46 { 47 static const char* const kDirectory = "/webviewCacheChromium"; 48 49 JNIEnv* env = JSC::Bindings::getJNIEnv(); 50 jclass bridgeClass = env->FindClass("android/webkit/JniUtil"); 51 jmethodID method = env->GetStaticMethodID(bridgeClass, "getCacheDirectory", "()Ljava/lang/String;"); 52 string storageDirectory = jstringToStdString(env, static_cast<jstring>(env->CallStaticObjectMethod(bridgeClass, method))); 53 env->DeleteLocalRef(bridgeClass); 54 55 // Return empty string if storageDirectory is an empty string 56 if (storageDirectory.empty()) 57 return storageDirectory; 58 59 storageDirectory.append(kDirectory); 60 return storageDirectory; 61 } 62 63 static scoped_refptr<WebCache>* instance(bool isPrivateBrowsing) 64 { 65 static scoped_refptr<WebCache> regularInstance; 66 static scoped_refptr<WebCache> privateInstance; 67 return isPrivateBrowsing ? &privateInstance : ®ularInstance; 68 } 69 70 WebCache* WebCache::get(bool isPrivateBrowsing) 71 { 72 MutexLocker lock(instanceMutex); 73 scoped_refptr<WebCache>* instancePtr = instance(isPrivateBrowsing); 74 if (!instancePtr->get()) 75 *instancePtr = new WebCache(isPrivateBrowsing); 76 return instancePtr->get(); 77 } 78 79 void WebCache::cleanup(bool isPrivateBrowsing) 80 { 81 MutexLocker lock(instanceMutex); 82 scoped_refptr<WebCache>* instancePtr = instance(isPrivateBrowsing); 83 *instancePtr = 0; 84 } 85 86 WebCache::WebCache(bool isPrivateBrowsing) 87 : m_doomAllEntriesCallback(this, &WebCache::doomAllEntries) 88 , m_onClearDoneCallback(this, &WebCache::onClearDone) 89 , m_isClearInProgress(false) 90 , m_openEntryCallback(this, &WebCache::openEntry) 91 , m_onGetEntryDoneCallback(this, &WebCache::onGetEntryDone) 92 , m_isGetEntryInProgress(false) 93 , m_cacheBackend(0) 94 { 95 base::Thread* ioThread = WebUrlLoaderClient::ioThread(); 96 scoped_refptr<base::MessageLoopProxy> cacheMessageLoopProxy = ioThread->message_loop_proxy(); 97 98 static const int kMaximumCacheSizeBytes = 20 * 1024 * 1024; 99 m_hostResolver = net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism, 0, 0); 100 101 m_proxyConfigService = new ProxyConfigServiceAndroid(); 102 net::HttpCache::BackendFactory* backendFactory; 103 if (isPrivateBrowsing) 104 backendFactory = net::HttpCache::DefaultBackend::InMemory(kMaximumCacheSizeBytes / 2); 105 else { 106 string storage(storageDirectory()); 107 if (storage.empty()) // Can't get a storage directory from the OS 108 backendFactory = net::HttpCache::DefaultBackend::InMemory(kMaximumCacheSizeBytes / 2); 109 else { 110 FilePath directoryPath(storage.c_str()); 111 backendFactory = new net::HttpCache::DefaultBackend(net::DISK_CACHE, directoryPath, kMaximumCacheSizeBytes, cacheMessageLoopProxy); 112 } 113 } 114 115 m_cache = new net::HttpCache(m_hostResolver.get(), 116 new CertVerifier(), 117 0, // dnsrr_resolver 118 0, // dns_cert_checker 119 net::ProxyService::CreateWithoutProxyResolver(m_proxyConfigService, 0 /* net_log */), 120 net::SSLConfigService::CreateSystemSSLConfigService(), 121 net::HttpAuthHandlerFactory::CreateDefault(m_hostResolver.get()), 122 0, // network_delegate 123 0, // net_log 124 backendFactory); 125 } 126 127 void WebCache::clear() 128 { 129 base::Thread* thread = WebUrlLoaderClient::ioThread(); 130 if (thread) 131 thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::clearImpl)); 132 } 133 134 void WebCache::certTrustChanged() 135 { 136 base::Thread* thread = WebUrlLoaderClient::ioThread(); 137 if (thread) 138 thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::certTrustChangedImpl)); 139 } 140 141 void WebCache::certTrustChangedImpl() 142 { 143 net::HttpNetworkSession* session = m_cache->GetSession(); 144 if (session) 145 session->cert_verifier()->ClearCache(); 146 m_cache->CloseAllConnections(); 147 } 148 149 void WebCache::closeIdleConnections() 150 { 151 base::Thread* thread = WebUrlLoaderClient::ioThread(); 152 if (thread) 153 thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::closeIdleImpl)); 154 } 155 156 void WebCache::closeIdleImpl() 157 { 158 m_cache->CloseIdleConnections(); 159 } 160 161 void WebCache::clearImpl() 162 { 163 if (m_isClearInProgress) 164 return; 165 m_isClearInProgress = true; 166 167 if (!m_cacheBackend) { 168 int code = m_cache->GetBackend(&m_cacheBackend, &m_doomAllEntriesCallback); 169 // Code ERR_IO_PENDING indicates that the operation is still in progress and 170 // the supplied callback will be invoked when it completes. 171 if (code == ERR_IO_PENDING) 172 return; 173 else if (code != OK) { 174 onClearDone(0 /*unused*/); 175 return; 176 } 177 } 178 doomAllEntries(0 /*unused*/); 179 } 180 181 void WebCache::doomAllEntries(int) 182 { 183 if (!m_cacheBackend) { 184 onClearDone(0 /*unused*/); 185 return; 186 } 187 188 // Code ERR_IO_PENDING indicates that the operation is still in progress and 189 // the supplied callback will be invoked when it completes. 190 if (m_cacheBackend->DoomAllEntries(&m_onClearDoneCallback) == ERR_IO_PENDING) 191 return; 192 onClearDone(0 /*unused*/); 193 } 194 195 void WebCache::onClearDone(int) 196 { 197 m_isClearInProgress = false; 198 } 199 200 scoped_refptr<CacheResult> WebCache::getCacheResult(String url) 201 { 202 // This is called on the UI thread. 203 MutexLocker lock(m_getEntryMutex); 204 if (m_isGetEntryInProgress) 205 return 0; // TODO: OK? Or can we queue 'em up? 206 207 // The Chromium methods are asynchronous, but we need this method to be 208 // synchronous. Do the work on the Chromium thread but block this thread 209 // here waiting for the work to complete. 210 base::Thread* thread = WebUrlLoaderClient::ioThread(); 211 if (!thread) 212 return 0; 213 214 m_entry = 0; 215 m_isGetEntryInProgress = true; 216 m_entryUrl = url.threadsafeCopy(); 217 thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::getEntryImpl)); 218 219 while (m_isGetEntryInProgress) 220 m_getEntryCondition.wait(m_getEntryMutex); 221 222 if (!m_entry) 223 return 0; 224 225 return new CacheResult(m_entry, url); 226 } 227 228 void WebCache::getEntryImpl() 229 { 230 if (!m_cacheBackend) { 231 int code = m_cache->GetBackend(&m_cacheBackend, &m_openEntryCallback); 232 if (code == ERR_IO_PENDING) 233 return; 234 else if (code != OK) { 235 onGetEntryDone(0 /*unused*/); 236 return; 237 } 238 } 239 openEntry(0 /*unused*/); 240 } 241 242 void WebCache::openEntry(int) 243 { 244 if (!m_cacheBackend) { 245 onGetEntryDone(0 /*unused*/); 246 return; 247 } 248 249 if (m_cacheBackend->OpenEntry(string(m_entryUrl.utf8().data()), &m_entry, &m_onGetEntryDoneCallback) == ERR_IO_PENDING) 250 return; 251 onGetEntryDone(0 /*unused*/); 252 } 253 254 void WebCache::onGetEntryDone(int) 255 { 256 // Unblock the UI thread in getEntry(); 257 MutexLocker lock(m_getEntryMutex); 258 m_isGetEntryInProgress = false; 259 m_getEntryCondition.signal(); 260 } 261 262 } // namespace android 263