Home | History | Annotate | Download | only in WebCoreSupport
      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 
     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 : &regularInstance;
     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::certTrustChanged()
    135 {
    136     base::Thread* thread = WebUrlLoaderClient::ioThread();
    137     if (thread)
    138         thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::certTrustChangedImpl));
    139 }
    140 
    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 
    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 
    156 void WebCache::closeIdleImpl()
    157 {
    158     m_cache->CloseIdleConnections();
    159 }
    160 
    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 
    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 
    195 void WebCache::onClearDone(int)
    196 {
    197     m_isClearInProgress = false;
    198 }
    199 
    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 
    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 
    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 
    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