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