blob: 82e3b275132cb75dc287e8b3500ce68a85371834 [file] [log] [blame]
/*
* Copyright 2010, The Android Open Source Project
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "WebCache.h"
#include "JNIUtility.h"
#include "WebCoreJni.h"
#include "WebRequestContext.h"
#include "WebUrlLoaderClient.h"
#include "net/http/http_network_session.h"
#include <wtf/text/CString.h>
using namespace WTF;
using namespace disk_cache;
using namespace net;
using namespace std;
namespace android {
static WTF::Mutex instanceMutex;
static string storageDirectory()
{
static const char* const kDirectory = "/webviewCacheChromium";
JNIEnv* env = JSC::Bindings::getJNIEnv();
jclass bridgeClass = env->FindClass("android/webkit/JniUtil");
jmethodID method = env->GetStaticMethodID(bridgeClass, "getCacheDirectory", "()Ljava/lang/String;");
string storageDirectory = jstringToStdString(env, static_cast<jstring>(env->CallStaticObjectMethod(bridgeClass, method)));
env->DeleteLocalRef(bridgeClass);
// Return empty string if storageDirectory is an empty string
if (storageDirectory.empty())
return storageDirectory;
storageDirectory.append(kDirectory);
return storageDirectory;
}
static scoped_refptr<WebCache>* instance(bool isPrivateBrowsing)
{
static scoped_refptr<WebCache> regularInstance;
static scoped_refptr<WebCache> privateInstance;
return isPrivateBrowsing ? &privateInstance : &regularInstance;
}
WebCache* WebCache::get(bool isPrivateBrowsing)
{
MutexLocker lock(instanceMutex);
scoped_refptr<WebCache>* instancePtr = instance(isPrivateBrowsing);
if (!instancePtr->get())
*instancePtr = new WebCache(isPrivateBrowsing);
return instancePtr->get();
}
void WebCache::cleanup(bool isPrivateBrowsing)
{
MutexLocker lock(instanceMutex);
scoped_refptr<WebCache>* instancePtr = instance(isPrivateBrowsing);
*instancePtr = 0;
}
WebCache::WebCache(bool isPrivateBrowsing)
: m_doomAllEntriesCallback(this, &WebCache::doomAllEntries)
, m_onClearDoneCallback(this, &WebCache::onClearDone)
, m_isClearInProgress(false)
, m_openEntryCallback(this, &WebCache::openEntry)
, m_onGetEntryDoneCallback(this, &WebCache::onGetEntryDone)
, m_isGetEntryInProgress(false)
, m_cacheBackend(0)
{
base::Thread* ioThread = WebUrlLoaderClient::ioThread();
scoped_refptr<base::MessageLoopProxy> cacheMessageLoopProxy = ioThread->message_loop_proxy();
static const int kMaximumCacheSizeBytes = 20 * 1024 * 1024;
m_hostResolver = net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism, 0, 0);
m_proxyConfigService = new ProxyConfigServiceAndroid();
net::HttpCache::BackendFactory* backendFactory;
if (isPrivateBrowsing)
backendFactory = net::HttpCache::DefaultBackend::InMemory(kMaximumCacheSizeBytes / 2);
else {
string storage(storageDirectory());
if (storage.empty()) // Can't get a storage directory from the OS
backendFactory = net::HttpCache::DefaultBackend::InMemory(kMaximumCacheSizeBytes / 2);
else {
FilePath directoryPath(storage.c_str());
backendFactory = new net::HttpCache::DefaultBackend(net::DISK_CACHE, directoryPath, kMaximumCacheSizeBytes, cacheMessageLoopProxy);
}
}
m_cache = new net::HttpCache(m_hostResolver.get(),
new CertVerifier(),
0, // dnsrr_resolver
0, // dns_cert_checker
net::ProxyService::CreateWithoutProxyResolver(m_proxyConfigService, 0 /* net_log */),
net::SSLConfigService::CreateSystemSSLConfigService(),
net::HttpAuthHandlerFactory::CreateDefault(m_hostResolver.get()),
0, // network_delegate
0, // net_log
backendFactory);
}
void WebCache::clear()
{
base::Thread* thread = WebUrlLoaderClient::ioThread();
if (thread)
thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::clearImpl));
}
void WebCache::certTrustChanged()
{
base::Thread* thread = WebUrlLoaderClient::ioThread();
if (thread)
thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::certTrustChangedImpl));
}
void WebCache::certTrustChangedImpl()
{
net::HttpNetworkSession* session = m_cache->GetSession();
if (session)
session->cert_verifier()->ClearCache();
m_cache->CloseAllConnections();
}
void WebCache::closeIdleConnections()
{
base::Thread* thread = WebUrlLoaderClient::ioThread();
if (thread)
thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::closeIdleImpl));
}
void WebCache::closeIdleImpl()
{
m_cache->CloseIdleConnections();
}
void WebCache::clearImpl()
{
if (m_isClearInProgress)
return;
m_isClearInProgress = true;
if (!m_cacheBackend) {
int code = m_cache->GetBackend(&m_cacheBackend, &m_doomAllEntriesCallback);
// Code ERR_IO_PENDING indicates that the operation is still in progress and
// the supplied callback will be invoked when it completes.
if (code == ERR_IO_PENDING)
return;
else if (code != OK) {
onClearDone(0 /*unused*/);
return;
}
}
doomAllEntries(0 /*unused*/);
}
void WebCache::doomAllEntries(int)
{
if (!m_cacheBackend) {
onClearDone(0 /*unused*/);
return;
}
// Code ERR_IO_PENDING indicates that the operation is still in progress and
// the supplied callback will be invoked when it completes.
if (m_cacheBackend->DoomAllEntries(&m_onClearDoneCallback) == ERR_IO_PENDING)
return;
onClearDone(0 /*unused*/);
}
void WebCache::onClearDone(int)
{
m_isClearInProgress = false;
}
scoped_refptr<CacheResult> WebCache::getCacheResult(String url)
{
// This is called on the UI thread.
MutexLocker lock(m_getEntryMutex);
if (m_isGetEntryInProgress)
return 0; // TODO: OK? Or can we queue 'em up?
// The Chromium methods are asynchronous, but we need this method to be
// synchronous. Do the work on the Chromium thread but block this thread
// here waiting for the work to complete.
base::Thread* thread = WebUrlLoaderClient::ioThread();
if (!thread)
return 0;
m_entry = 0;
m_isGetEntryInProgress = true;
m_entryUrl = url.threadsafeCopy();
thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::getEntryImpl));
while (m_isGetEntryInProgress)
m_getEntryCondition.wait(m_getEntryMutex);
if (!m_entry)
return 0;
return new CacheResult(m_entry, url);
}
void WebCache::getEntryImpl()
{
if (!m_cacheBackend) {
int code = m_cache->GetBackend(&m_cacheBackend, &m_openEntryCallback);
if (code == ERR_IO_PENDING)
return;
else if (code != OK) {
onGetEntryDone(0 /*unused*/);
return;
}
}
openEntry(0 /*unused*/);
}
void WebCache::openEntry(int)
{
if (!m_cacheBackend) {
onGetEntryDone(0 /*unused*/);
return;
}
if (m_cacheBackend->OpenEntry(string(m_entryUrl.utf8().data()), &m_entry, &m_onGetEntryDoneCallback) == ERR_IO_PENDING)
return;
onGetEntryDone(0 /*unused*/);
}
void WebCache::onGetEntryDone(int)
{
// Unblock the UI thread in getEntry();
MutexLocker lock(m_getEntryMutex);
m_isGetEntryInProgress = false;
m_getEntryCondition.signal();
}
} // namespace android