• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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