OSDN Git Service

Hook up CacheManager for the Chromium HTTP stack
authorSteve Block <steveblock@google.com>
Thu, 6 Jan 2011 17:36:11 +0000 (17:36 +0000)
committerBen Murdoch <benm@google.com>
Fri, 14 Jan 2011 16:47:02 +0000 (16:47 +0000)
Requires a corresponding change in frameworks/base
https://android-git.corp.google.com/g/87939

Bug: 3270236
Change-Id: I31a640de86d6fad00eb8b71784e0ee9136ab1174

WebKit/Android.mk
WebKit/android/WebCoreSupport/CacheResult.cpp [new file with mode: 0644]
WebKit/android/WebCoreSupport/CacheResult.h [new file with mode: 0644]
WebKit/android/WebCoreSupport/WebCache.cpp
WebKit/android/WebCoreSupport/WebCache.h
WebKit/android/jni/CacheManager.cpp [new file with mode: 0644]
WebKit/android/jni/WebCoreJniOnLoad.cpp

index f8ecd56..b87e981 100644 (file)
@@ -36,6 +36,7 @@ LOCAL_SRC_FILES := \
 ifeq ($(HTTP_STACK),chrome)
 LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \
        android/WebCoreSupport/ChromiumInit.cpp \
+       android/WebCoreSupport/CacheResult.cpp \
        android/WebCoreSupport/WebCache.cpp \
        android/WebCoreSupport/WebCookieJar.cpp \
        android/WebCoreSupport/WebUrlLoader.cpp \
@@ -60,6 +61,7 @@ LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \
        \
        android/icu/unicode/ucnv.cpp \
        \
+       android/jni/CacheManager.cpp \
        android/jni/CookieManager.cpp \
        android/jni/DeviceMotionAndOrientationManager.cpp \
        android/jni/DeviceMotionClientImpl.cpp \
diff --git a/WebKit/android/WebCoreSupport/CacheResult.cpp b/WebKit/android/WebCoreSupport/CacheResult.cpp
new file mode 100644 (file)
index 0000000..8193ac1
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2011, 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 "CacheResult.h"
+
+#include "WebUrlLoaderClient.h"
+#include <platform/FileSystem.h>
+
+using namespace base;
+using namespace disk_cache;
+using namespace net;
+using namespace std;
+
+namespace android {
+
+// All public methods are called on a UI thread but we do work on the
+// Chromium thread. However, because we block the WebCore thread while this
+// work completes, we can never receive new public method calls while the
+// Chromium thread work is in progress.
+
+// Copied from HttpCache
+enum {
+    kResponseInfoIndex = 0,
+    kResponseContentIndex
+};
+
+CacheResult::CacheResult(disk_cache::Entry* entry)
+    : m_entry(entry)
+    , m_onResponseHeadersDoneCallback(this, &CacheResult::onResponseHeadersDone)
+    , m_onReadNextChunkDoneCallback(this, &CacheResult::onReadNextChunkDone)
+{
+    ASSERT(m_entry);
+}
+
+CacheResult::~CacheResult()
+{
+    m_entry->Close();
+    // TODO: Should we also call DoneReadingFromEntry() on the cache for our
+    // entry?
+}
+
+int64 CacheResult::contentSize() const
+{
+    // The android stack does not take the content length from the HTTP response
+    // headers but calculates it when writing the content to disk. It can never
+    // overflow a long because we limit the cache size.
+    return m_entry->GetDataSize(kResponseContentIndex);
+}
+
+bool CacheResult::firstResponseHeader(const char* name, String* result, bool allowEmptyString) const
+{
+    string value;
+    if (responseHeaders() && responseHeaders()->EnumerateHeader(NULL, name, &value) && (!value.empty() || allowEmptyString)) {
+        *result = String(value.c_str());
+        return true;
+    }
+    return false;
+}
+
+String CacheResult::mimeType() const
+{
+    string mimeType;
+    responseHeaders()->GetMimeType(&mimeType);
+    return String(mimeType.c_str());
+}
+
+int64 CacheResult::expires() const
+{
+     // We have to do this manually, rather than using HttpResponseHeaders::GetExpiresValue(),
+     // to handle the "-1" and "0" special cases.
+     string expiresString;
+     if (responseHeaders() && responseHeaders()->EnumerateHeader(NULL, "expires", &expiresString)) {
+         wstring expiresStringWide(expiresString.begin(), expiresString.end());  // inflate ascii
+         // We require the time expressed as ms since the epoch.
+         Time time;
+         if (Time::FromString(expiresStringWide.c_str(), &time)) {
+             // Will not overflow for a very long time!
+             return static_cast<int64>(1000.0 * time.ToDoubleT());
+         }
+
+         if (expiresString == "-1" || expiresString == "0")
+             return 0;
+     }
+
+     // TODO
+     // The Android stack applies a heuristic to set an expiry date if the
+     // expires header is not set or can't be parsed. I'm  not sure whether the Chromium cache
+     // does this, and if so, it may not be possible for us to get hold of it
+     // anyway to set it on the result.
+     return -1;
+}
+
+int CacheResult::responseCode() const
+{
+    return responseHeaders() ? responseHeaders()->response_code() : 0;
+}
+
+bool CacheResult::writeToFile(const String& filePath) const
+{
+    // Getting the headers is potentially async, so post to the Chromium thread
+    // and block here.
+    MutexLocker lock(m_mutex);
+
+    base::Thread* thread = WebUrlLoaderClient::ioThread();
+    if (!thread)
+        return false;
+
+    CacheResult* me = const_cast<CacheResult*>(this);
+    thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(me, &CacheResult::writeToFileImpl));
+
+    m_filePath = filePath.threadsafeCopy();
+    m_isAsyncOperationInProgress = true;
+    while (m_isAsyncOperationInProgress)
+        m_condition.wait(m_mutex);
+
+    return m_wasWriteToFileSuccessful;
+}
+
+void CacheResult::writeToFileImpl()
+{
+    m_bufferSize = m_entry->GetDataSize(kResponseContentIndex);
+    m_readOffset = 0;
+    m_wasWriteToFileSuccessful = false;
+    readNextChunk();
+}
+
+void CacheResult::readNextChunk()
+{
+    m_buffer = new IOBuffer(m_bufferSize);
+    int rv = m_entry->ReadData(kResponseInfoIndex, m_readOffset, m_buffer, m_bufferSize, &m_onReadNextChunkDoneCallback);
+    if (rv == ERR_IO_PENDING)
+        return;
+
+    onReadNextChunkDone(rv);
+};
+
+void CacheResult::onReadNextChunkDone(int size)
+{
+    if (size > 0) {
+        // Still more reading to be done.
+        if (writeChunkToFile()) {
+            // TODO: I assume that we need to clear and resize the buffer for the next read?
+            m_readOffset += size;
+            m_bufferSize -= size;
+            readNextChunk();
+        } else
+            onWriteToFileDone();
+        return;
+    }
+
+    if (!size) {
+        // Reached end of file.
+        if (writeChunkToFile())
+            m_wasWriteToFileSuccessful = true;
+    }
+    onWriteToFileDone();
+}
+
+bool CacheResult::writeChunkToFile()
+{
+    PlatformFileHandle file;
+    file = openFile(m_filePath, OpenForWrite);
+    if (!isHandleValid(file))
+        return false;
+    return WebCore::writeToFile(file, m_buffer->data(), m_bufferSize) == m_bufferSize;
+}
+
+void CacheResult::onWriteToFileDone()
+{
+    MutexLocker lock(m_mutex);
+    m_isAsyncOperationInProgress = false;
+    m_condition.signal();
+}
+
+HttpResponseHeaders* CacheResult::responseHeaders() const
+{
+    MutexLocker lock(m_mutex);
+    if (m_responseHeaders)
+        return m_responseHeaders;
+
+    // Getting the headers is potentially async, so post to the Chromium thread
+    // and block here.
+    base::Thread* thread = WebUrlLoaderClient::ioThread();
+    if (!thread)
+        return 0;
+
+    CacheResult* me = const_cast<CacheResult*>(this);
+    thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(me, &CacheResult::responseHeadersImpl));
+
+    m_isAsyncOperationInProgress = true;
+    while (m_isAsyncOperationInProgress)
+        m_condition.wait(m_mutex);
+
+    return m_responseHeaders;
+}
+
+void CacheResult::responseHeadersImpl()
+{
+    m_bufferSize = m_entry->GetDataSize(kResponseInfoIndex);
+    m_buffer = new IOBuffer(m_bufferSize);
+
+    int rv = m_entry->ReadData(kResponseInfoIndex, 0, m_buffer, m_bufferSize, &m_onResponseHeadersDoneCallback);
+    if (rv == ERR_IO_PENDING)
+        return;
+
+    onResponseHeadersDone(rv);
+};
+
+void CacheResult::onResponseHeadersDone(int size)
+{
+    MutexLocker lock(m_mutex);
+    // It's OK to throw away the HttpResponseInfo object as we hold our own ref
+    // to the headers.
+    HttpResponseInfo response;
+    bool truncated = false; // TODO: Waht is this param for?
+    if (size == m_bufferSize && HttpCache::ParseResponseInfo(m_buffer->data(), m_bufferSize, &response, &truncated))
+        m_responseHeaders = response.headers;
+    m_isAsyncOperationInProgress = false;
+    m_condition.signal();
+}
+
+} // namespace android
diff --git a/WebKit/android/WebCoreSupport/CacheResult.h b/WebKit/android/WebCoreSupport/CacheResult.h
new file mode 100644 (file)
index 0000000..02dced2
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2011, 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.
+ */
+
+#ifndef CacheResult_h
+#define CacheResult_h
+
+#include "ChromiumIncludes.h"
+
+#include <wtf/RefCounted.h>
+#include <wtf/ThreadingPrimitives.h>
+#include <wtf/text/WTFString.h>
+
+namespace android {
+
+// A wrapper around a disk_cache::Entry. Provides fields appropriate for constructing a Java CacheResult object.
+class CacheResult : public base::RefCountedThreadSafe<CacheResult> {
+public:
+    // Takes ownership of the Entry passed to the constructor.
+    CacheResult(disk_cache::Entry*);
+    ~CacheResult();
+
+    int64 contentSize() const;
+    bool firstResponseHeader(const char* name, WTF::String* result, bool allowEmptyString) const;
+    // The Android stack uses the empty string if no Content-Type headers are
+    // found, so we use the same default here.
+    WTF::String mimeType() const;
+    // Returns the value of the expires header as milliseconds since the epoch.
+    int64 expires() const;
+    int responseCode() const;
+    bool writeToFile(const WTF::String& filePath) const;
+private:
+    net::HttpResponseHeaders* responseHeaders() const;
+    void responseHeadersImpl();
+    void onResponseHeadersDone(int size);
+
+    void writeToFileImpl();
+    void readNextChunk();
+    void onReadNextChunkDone(int size);
+    bool writeChunkToFile();
+    void onWriteToFileDone();
+
+    disk_cache::Entry* m_entry;
+
+    scoped_refptr<net::HttpResponseHeaders> m_responseHeaders;
+
+    int m_readOffset;
+    bool m_wasWriteToFileSuccessful;
+    mutable String m_filePath;
+
+    int m_bufferSize;
+    scoped_refptr<net::IOBuffer> m_buffer;
+    mutable bool m_isAsyncOperationInProgress;
+    mutable WTF::Mutex m_mutex;
+    mutable WTF::ThreadCondition m_condition;
+
+    net::CompletionCallbackImpl<CacheResult> m_onResponseHeadersDoneCallback;
+    net::CompletionCallbackImpl<CacheResult> m_onReadNextChunkDoneCallback;
+};
+
+} // namespace android
+
+#endif
index 07031c8..0b9d150 100644 (file)
 #include "WebRequestContext.h"
 #include "WebUrlLoaderClient.h"
 
+#include <wtf/text/CString.h>
+
 using namespace WTF;
+using namespace disk_cache;
 using namespace net;
 using namespace std;
 
@@ -92,6 +95,9 @@ 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();
@@ -166,4 +172,66 @@ 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);
+}
+
+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
index d89cba7..7149fcc 100644 (file)
 #ifndef WebCache_h
 #define WebCache_h
 
+#include "CacheResult.h"
 #include "ChromiumIncludes.h"
 
 #include <OwnPtr.h>
+#include <platform/text/PlatformString.h>
 #include <wtf/ThreadingPrimitives.h>
 
 namespace android {
@@ -41,6 +43,7 @@ public:
     static void cleanup(bool isPrivateBrowsing);
 
     void clear();
+    scoped_refptr<CacheResult> getCacheResult(WTF::String url);
     net::HostResolver* hostResolver() { return m_hostResolver.get(); }
     net::HttpCache* cache() { return m_cache.get(); }
     net::ProxyConfigServiceAndroid* proxy() { return m_proxyConfigService; }
@@ -53,6 +56,11 @@ private:
     void doomAllEntries(int);
     void onClearDone(int);
 
+    // For getEntry()
+    void getEntryImpl();
+    void openEntry(int);
+    void onGetEntryDone(int);
+
     OwnPtr<net::HostResolver> m_hostResolver;
     OwnPtr<net::HttpCache> m_cache;
     // This is owned by the ProxyService, which is owned by the HttpNetworkLayer,
@@ -62,8 +70,17 @@ private:
     // For clear()
     net::CompletionCallbackImpl<WebCache> m_doomAllEntriesCallback;
     net::CompletionCallbackImpl<WebCache> m_onClearDoneCallback;
-    disk_cache::Backend* m_cacheBackend;
     bool m_isClearInProgress;
+    // For getEntry()
+    net::CompletionCallbackImpl<WebCache> m_openEntryCallback;
+    net::CompletionCallbackImpl<WebCache> m_onGetEntryDoneCallback;
+    bool m_isGetEntryInProgress;
+    String m_entryUrl;
+    disk_cache::Entry* m_entry;
+    WTF::Mutex m_getEntryMutex;
+    WTF::ThreadCondition m_getEntryCondition;
+
+    disk_cache::Backend* m_cacheBackend;
 };
 
 } // namespace android
diff --git a/WebKit/android/jni/CacheManager.cpp b/WebKit/android/jni/CacheManager.cpp
new file mode 100644 (file)
index 0000000..144b62a
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2011, 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"
+
+#if USE(CHROME_NETWORK_STACK)
+
+#include "ChromiumIncludes.h"
+#include "WebCache.h"
+#include "WebCoreJni.h"
+
+#include <JNIHelp.h>
+#include <platform/FileSystem.h>
+#include <platform/text/Base64.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/WTFString.h>
+
+using namespace WebCore;
+using namespace base;
+using namespace disk_cache;
+using namespace net;
+using namespace std;
+
+namespace android {
+
+// JNI for android.webkit.CacheManager
+static const char* javaCacheManagerClass = "android/webkit/CacheManager";
+
+static void setStringField(JNIEnv* env, const jobject& object, const jfieldID& field, const String& str)
+{
+    jstring jstr = wtfStringToJstring(env, str);
+    env->SetObjectField(object, field, jstr);
+    env->DeleteLocalRef(jstr);
+}
+
+static void setFieldFromHeaderIfPresent(CacheResult* result, const char* header, JNIEnv* env, const jobject& object, const jfieldID& field, bool allowEmptyString)
+{
+  String value;
+  if (result->firstResponseHeader(header, &value, allowEmptyString))
+      setStringField(env, object, field, value);
+}
+
+static String getCacheFileBaseDir(JNIEnv* env)
+{
+    static String baseDir;
+    if (baseDir.isEmpty()) {
+        jclass cacheManagerClass = env->FindClass("android/webkit/CacheManager");
+        jmethodID getCacheFileBaseDirMethod = env->GetStaticMethodID(cacheManagerClass, "getCacheFileBaseDir", "()Ljava/io/File;");
+        jclass fileClass = env->FindClass("java/io/File");
+        jmethodID getPathMethod = env->GetMethodID(fileClass, "getPath", "()Ljava/lang/String;");
+        jobject fileObject = env->CallStaticObjectMethod(cacheManagerClass, getCacheFileBaseDirMethod);
+        baseDir = jstringToWtfString(env, static_cast<jstring>(env->CallObjectMethod(fileObject, getPathMethod)));
+    }
+    return baseDir;
+}
+
+static jobject getCacheResult(JNIEnv* env, jobject, jstring url)
+{
+    // This is called on the UI thread.
+    scoped_refptr<CacheResult> result = WebCache::get(false /*privateBrowsing*/)->getCacheResult(jstringToWtfString(env, url));
+    if (!result)
+        return 0;
+
+    // We create and populate a file with the cache entry. This allows us to
+    // replicate the behaviour of the Android HTTP stack in the Java
+    // CacheManager, which opens the cache file and provides an input stream to
+    // the file as part of the Java CacheResult object!
+    String urlWtfString = jstringToWtfString(env, url);
+    Vector<char> encodedUrl;
+    base64Encode(urlWtfString.utf8().data(), urlWtfString.length(), encodedUrl, false /*insertLFs*/);
+    String filePath = pathByAppendingComponent(getCacheFileBaseDir(env), encodedUrl.data());
+    if (!result->writeToFile(filePath))
+        return 0;
+
+    jclass cacheResultClass = env->FindClass("android/webkit/CacheManager$CacheResult");
+    jmethodID constructor = env->GetMethodID(cacheResultClass, "<init>", "()V");
+    // We only bother with the fields that are made accessible through the public API.
+    jfieldID contentdispositionField = env->GetFieldID(cacheResultClass, "contentdisposition", "Ljava/lang/String;");
+    jfieldID contentLengthField = env->GetFieldID(cacheResultClass, "contentLength", "J");
+    jfieldID etagField = env->GetFieldID(cacheResultClass, "etag", "Ljava/lang/String;");
+    jfieldID encodingField = env->GetFieldID(cacheResultClass, "encoding", "Ljava/lang/String;");
+    jfieldID expiresField = env->GetFieldID(cacheResultClass, "expires", "J");
+    jfieldID expiresStringField = env->GetFieldID(cacheResultClass, "expiresString", "Ljava/lang/String;");
+    jfieldID httpStatusCodeField = env->GetFieldID(cacheResultClass, "httpStatusCode", "I");
+    jfieldID lastModifiedField = env->GetFieldID(cacheResultClass, "lastModified", "Ljava/lang/String;");
+    jfieldID localPathField = env->GetFieldID(cacheResultClass, "localPath", "Ljava/lang/String;");
+    jfieldID locationField = env->GetFieldID(cacheResultClass, "location", "Ljava/lang/String;");
+    jfieldID mimeTypeField = env->GetFieldID(cacheResultClass, "mimeType", "Ljava/lang/String;");
+
+    jobject javaResult = env->NewObject(cacheResultClass, constructor);
+    setFieldFromHeaderIfPresent(result.get(), "content-disposition", env, javaResult, contentdispositionField, true);
+    env->SetLongField(javaResult, contentLengthField, result->contentSize());
+    setFieldFromHeaderIfPresent(result.get(), "etag", env, javaResult, etagField, false);
+    setStringField(env, javaResult, encodingField, "TODO"); // TODO: Where does the Android stack set this?
+    env->SetLongField(javaResult, expiresField, result->expires());
+    env->SetIntField(javaResult, httpStatusCodeField, result->responseCode());
+    setFieldFromHeaderIfPresent(result.get(), "last-modified", env, javaResult, lastModifiedField, false);
+    setStringField(env, javaResult, localPathField, encodedUrl.data());
+    setFieldFromHeaderIfPresent(result.get(), "location", env, javaResult, locationField, false);
+    setStringField(env, javaResult, mimeTypeField, result->mimeType());
+
+    return javaResult;
+}
+
+static JNINativeMethod gCacheManagerMethods[] = {
+    { "nativeGetCacheResult", "(Ljava/lang/String;)Landroid/webkit/CacheManager$CacheResult;", (void*) getCacheResult },
+};
+
+int registerCacheManager(JNIEnv* env)
+{
+#ifndef NDEBUG
+    jclass cacheManager = env->FindClass(javaCacheManagerClass);
+    LOG_ASSERT(cacheManager, "Unable to find class");
+    env->DeleteLocalRef(cacheManager);
+#endif
+    return jniRegisterNativeMethods(env, javaCacheManagerClass, gCacheManagerMethods, NELEM(gCacheManagerMethods));
+}
+
+} // namespace android
+
+#endif //  USE(CHROME_NETWORK_STACK)
index f512604..1f264a2 100644 (file)
@@ -93,6 +93,9 @@ extern int registerMediaPlayerVideo(JNIEnv*);
 #endif
 extern int registerDeviceMotionAndOrientationManager(JNIEnv*);
 extern int registerCookieManager(JNIEnv*);
+#if USE(CHROME_NETWORK_STACK)
+extern int registerCacheManager(JNIEnv*);
+#endif
 
 }
 
@@ -122,6 +125,9 @@ static RegistrationMethod gWebCoreRegMethods[] = {
 #endif
     { "DeviceMotionAndOrientationManager", android::registerDeviceMotionAndOrientationManager },
     { "CookieManager", android::registerCookieManager },
+#if USE(CHROME_NETWORK_STACK)
+    { "CacheManager", android::registerCacheManager },
+#endif
 };
 
 EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)