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
setStringField(JNIEnv * env,const jobject & object,const jfieldID & field,const String & str)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
setFieldFromHeaderIfPresent(CacheResult * result,const char * header,JNIEnv * env,const jobject & object,const jfieldID & field,bool allowEmptyString)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
getCacheFileBaseDir(JNIEnv * env)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
getCacheResult(JNIEnv * env,jobject,jstring url)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
registerCacheManager(JNIEnv * env)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