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
storageDirectory()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
instance(bool isPrivateBrowsing)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
get(bool isPrivateBrowsing)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
cleanup(bool isPrivateBrowsing)79 void WebCache::cleanup(bool isPrivateBrowsing)
80 {
81 MutexLocker lock(instanceMutex);
82 scoped_refptr<WebCache>* instancePtr = instance(isPrivateBrowsing);
83 *instancePtr = 0;
84 }
85
WebCache(bool isPrivateBrowsing)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
clear()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
certTrustChanged()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
certTrustChangedImpl()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
closeIdleConnections()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
closeIdleImpl()156 void WebCache::closeIdleImpl()
157 {
158 m_cache->CloseIdleConnections();
159 }
160
clearImpl()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
doomAllEntries(int)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
onClearDone(int)195 void WebCache::onClearDone(int)
196 {
197 m_isClearInProgress = false;
198 }
199
getCacheResult(String url)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
getEntryImpl()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
openEntry(int)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
onGetEntryDone(int)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