/* * 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 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(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* instance(bool isPrivateBrowsing) { static scoped_refptr regularInstance; static scoped_refptr privateInstance; return isPrivateBrowsing ? &privateInstance : ®ularInstance; } WebCache* WebCache::get(bool isPrivateBrowsing) { MutexLocker lock(instanceMutex); scoped_refptr* instancePtr = instance(isPrivateBrowsing); if (!instancePtr->get()) *instancePtr = new WebCache(isPrivateBrowsing); return instancePtr->get(); } void WebCache::cleanup(bool isPrivateBrowsing) { MutexLocker lock(instanceMutex); scoped_refptr* 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 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 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