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 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::closeIdleConnections() 135 { 136 base::Thread* thread = WebUrlLoaderClient::ioThread(); 137 if (thread) 138 thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::closeIdleImpl)); 139 } 140 141 void WebCache::closeIdleImpl() 142 { 143 m_cache->CloseIdleConnections(); 144 } 145 146 void WebCache::clearImpl() 147 { 148 if (m_isClearInProgress) 149 return; 150 m_isClearInProgress = true; 151 152 if (!m_cacheBackend) { 153 int code = m_cache->GetBackend(&m_cacheBackend, &m_doomAllEntriesCallback); 154 // Code ERR_IO_PENDING indicates that the operation is still in progress and 155 // the supplied callback will be invoked when it completes. 156 if (code == ERR_IO_PENDING) 157 return; 158 else if (code != OK) { 159 onClearDone(0 /*unused*/); 160 return; 161 } 162 } 163 doomAllEntries(0 /*unused*/); 164 } 165 166 void WebCache::doomAllEntries(int) 167 { 168 if (!m_cacheBackend) { 169 onClearDone(0 /*unused*/); 170 return; 171 } 172 173 // Code ERR_IO_PENDING indicates that the operation is still in progress and 174 // the supplied callback will be invoked when it completes. 175 if (m_cacheBackend->DoomAllEntries(&m_onClearDoneCallback) == ERR_IO_PENDING) 176 return; 177 onClearDone(0 /*unused*/); 178 } 179 180 void WebCache::onClearDone(int) 181 { 182 m_isClearInProgress = false; 183 } 184 185 scoped_refptr<CacheResult> WebCache::getCacheResult(String url) 186 { 187 // This is called on the UI thread. 188 MutexLocker lock(m_getEntryMutex); 189 if (m_isGetEntryInProgress) 190 return 0; // TODO: OK? Or can we queue 'em up? 191 192 // The Chromium methods are asynchronous, but we need this method to be 193 // synchronous. Do the work on the Chromium thread but block this thread 194 // here waiting for the work to complete. 195 base::Thread* thread = WebUrlLoaderClient::ioThread(); 196 if (!thread) 197 return 0; 198 199 m_entry = 0; 200 m_isGetEntryInProgress = true; 201 m_entryUrl = url.threadsafeCopy(); 202 thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::getEntryImpl)); 203 204 while (m_isGetEntryInProgress) 205 m_getEntryCondition.wait(m_getEntryMutex); 206 207 if (!m_entry) 208 return 0; 209 210 return new CacheResult(m_entry, url); 211 } 212 213 void WebCache::getEntryImpl() 214 { 215 if (!m_cacheBackend) { 216 int code = m_cache->GetBackend(&m_cacheBackend, &m_openEntryCallback); 217 if (code == ERR_IO_PENDING) 218 return; 219 else if (code != OK) { 220 onGetEntryDone(0 /*unused*/); 221 return; 222 } 223 } 224 openEntry(0 /*unused*/); 225 } 226 227 void WebCache::openEntry(int) 228 { 229 if (!m_cacheBackend) { 230 onGetEntryDone(0 /*unused*/); 231 return; 232 } 233 234 if (m_cacheBackend->OpenEntry(string(m_entryUrl.utf8().data()), &m_entry, &m_onGetEntryDoneCallback) == ERR_IO_PENDING) 235 return; 236 onGetEntryDone(0 /*unused*/); 237 } 238 239 void WebCache::onGetEntryDone(int) 240 { 241 // Unblock the UI thread in getEntry(); 242 MutexLocker lock(m_getEntryMutex); 243 m_isGetEntryInProgress = false; 244 m_getEntryCondition.signal(); 245 } 246 247 } // namespace android 248