Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright 2011, 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 
     28 #include "ChromiumIncludes.h"
     29 #include "WebCache.h"
     30 #include "WebCoreJni.h"
     31 
     32 #include <JNIHelp.h>
     33 #include <platform/FileSystem.h>
     34 #include <platform/text/Base64.h>
     35 #include <wtf/text/CString.h>
     36 #include <wtf/text/WTFString.h>
     37 
     38 using namespace WebCore;
     39 using namespace base;
     40 using namespace disk_cache;
     41 using namespace net;
     42 using namespace std;
     43 
     44 namespace android {
     45 
     46 // JNI for android.webkit.CacheManager
     47 static const char* javaCacheManagerClass = "android/webkit/CacheManager";
     48 
     49 static void setStringField(JNIEnv* env, const jobject& object, const jfieldID& field, const String& str)
     50 {
     51     jstring jstr = wtfStringToJstring(env, str);
     52     env->SetObjectField(object, field, jstr);
     53     env->DeleteLocalRef(jstr);
     54 }
     55 
     56 static void setFieldFromHeaderIfPresent(CacheResult* result, const char* header, JNIEnv* env, const jobject& object, const jfieldID& field, bool allowEmptyString)
     57 {
     58   String value;
     59   if (result->firstResponseHeader(header, &value, allowEmptyString))
     60       setStringField(env, object, field, value);
     61 }
     62 
     63 static String getCacheFileBaseDir(JNIEnv* env)
     64 {
     65     static String baseDir;
     66     if (baseDir.isEmpty()) {
     67         jclass cacheManagerClass = env->FindClass("android/webkit/CacheManager");
     68         jmethodID getCacheFileBaseDirMethod = env->GetStaticMethodID(cacheManagerClass, "getCacheFileBaseDir", "()Ljava/io/File;");
     69         jclass fileClass = env->FindClass("java/io/File");
     70         jmethodID getPathMethod = env->GetMethodID(fileClass, "getPath", "()Ljava/lang/String;");
     71         jobject fileObject = env->CallStaticObjectMethod(cacheManagerClass, getCacheFileBaseDirMethod);
     72         baseDir = jstringToWtfString(env, static_cast<jstring>(env->CallObjectMethod(fileObject, getPathMethod)));
     73     }
     74     return baseDir;
     75 }
     76 
     77 static jobject getCacheResult(JNIEnv* env, jobject, jstring url)
     78 {
     79     // This is called on the UI thread.
     80     scoped_refptr<CacheResult> result = WebCache::get(false /*privateBrowsing*/)->getCacheResult(jstringToWtfString(env, url));
     81     if (!result)
     82         return 0;
     83 
     84     // We create and populate a file with the cache entry. This allows us to
     85     // replicate the behaviour of the Android HTTP stack in the Java
     86     // CacheManager, which opens the cache file and provides an input stream to
     87     // the file as part of the Java CacheResult object!
     88     String urlWtfString = jstringToWtfString(env, url);
     89     Vector<char> encodedUrl;
     90     base64Encode(urlWtfString.utf8().data(), urlWtfString.length(), encodedUrl, false /*insertLFs*/);
     91     encodedUrl.append('\0');
     92     String filePath = pathByAppendingComponent(getCacheFileBaseDir(env), encodedUrl.data());
     93     if (!result->writeToFile(filePath))
     94         return 0;
     95 
     96     jclass cacheResultClass = env->FindClass("android/webkit/CacheManager$CacheResult");
     97     jmethodID constructor = env->GetMethodID(cacheResultClass, "<init>", "()V");
     98     // We only bother with the fields that are made accessible through the public API.
     99     jfieldID contentdispositionField = env->GetFieldID(cacheResultClass, "contentdisposition", "Ljava/lang/String;");
    100     jfieldID contentLengthField = env->GetFieldID(cacheResultClass, "contentLength", "J");
    101     jfieldID etagField = env->GetFieldID(cacheResultClass, "etag", "Ljava/lang/String;");
    102     jfieldID encodingField = env->GetFieldID(cacheResultClass, "encoding", "Ljava/lang/String;");
    103     jfieldID expiresField = env->GetFieldID(cacheResultClass, "expires", "J");
    104     jfieldID expiresStringField = env->GetFieldID(cacheResultClass, "expiresString", "Ljava/lang/String;");
    105     jfieldID httpStatusCodeField = env->GetFieldID(cacheResultClass, "httpStatusCode", "I");
    106     jfieldID lastModifiedField = env->GetFieldID(cacheResultClass, "lastModified", "Ljava/lang/String;");
    107     jfieldID localPathField = env->GetFieldID(cacheResultClass, "localPath", "Ljava/lang/String;");
    108     jfieldID locationField = env->GetFieldID(cacheResultClass, "location", "Ljava/lang/String;");
    109     jfieldID mimeTypeField = env->GetFieldID(cacheResultClass, "mimeType", "Ljava/lang/String;");
    110 
    111     jobject javaResult = env->NewObject(cacheResultClass, constructor);
    112     setFieldFromHeaderIfPresent(result.get(), "content-disposition", env, javaResult, contentdispositionField, true);
    113     env->SetLongField(javaResult, contentLengthField, result->contentSize());
    114     setFieldFromHeaderIfPresent(result.get(), "etag", env, javaResult, etagField, false);
    115     setStringField(env, javaResult, encodingField, "TODO"); // TODO: Where does the Android stack set this?
    116     env->SetLongField(javaResult, expiresField, result->expires());
    117     env->SetIntField(javaResult, httpStatusCodeField, result->responseCode());
    118     setFieldFromHeaderIfPresent(result.get(), "last-modified", env, javaResult, lastModifiedField, false);
    119     setStringField(env, javaResult, localPathField, encodedUrl.data());
    120     setFieldFromHeaderIfPresent(result.get(), "location", env, javaResult, locationField, false);
    121     setStringField(env, javaResult, mimeTypeField, result->mimeType());
    122 
    123     return javaResult;
    124 }
    125 
    126 static JNINativeMethod gCacheManagerMethods[] = {
    127     { "nativeGetCacheResult", "(Ljava/lang/String;)Landroid/webkit/CacheManager$CacheResult;", (void*) getCacheResult },
    128 };
    129 
    130 int registerCacheManager(JNIEnv* env)
    131 {
    132 #ifndef NDEBUG
    133     jclass cacheManager = env->FindClass(javaCacheManagerClass);
    134     ALOG_ASSERT(cacheManager, "Unable to find class");
    135     env->DeleteLocalRef(cacheManager);
    136 #endif
    137     return jniRegisterNativeMethods(env, javaCacheManagerClass, gCacheManagerMethods, NELEM(gCacheManagerMethods));
    138 }
    139 
    140 } // namespace android
    141