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