1 /*
2 * Copyright 2006, 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 APPLE COMPUTER, INC. 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 #define LOG_TAG "webcoreglue"
27
28 #include <config.h>
29 #include <wtf/Platform.h>
30
31 #include "jni_utility.h"
32 #include "WebCoreResourceLoader.h"
33 #include "SkUtils.h"
34
35 #include "CString.h"
36 #include "ResourceError.h"
37 #include "ResourceHandle.h"
38 #include "ResourceHandleClient.h"
39 #include "ResourceHandleInternal.h"
40 #include "ResourceResponse.h"
41 #include "WebCoreJni.h"
42
43 #ifdef ANDROID_INSTRUMENT
44 #include "TimeCounter.h"
45 #endif
46
47 #include <utils/misc.h>
48 #include <JNIHelp.h>
49 #include <SkTypes.h>
50 #include <stdlib.h>
51
52 namespace android {
53
54 // ----------------------------------------------------------------------------
55
56 static struct resourceloader_t {
57 jfieldID mObject;
58 jmethodID mCancelMethodID;
59 jmethodID mDownloadFileMethodID;
60 jmethodID mWillLoadFromCacheMethodID;
61 } gResourceLoader;
62
63 // ----------------------------------------------------------------------------
64
65 #define GET_NATIVE_HANDLE(env, obj) ((WebCore::ResourceHandle*)env->GetIntField(obj, gResourceLoader.mObject))
66 #define SET_NATIVE_HANDLE(env, obj, handle) (env->SetIntField(obj, gResourceLoader.mObject, handle))
67
68 //-----------------------------------------------------------------------------
69 // ResourceLoadHandler
70
WebCoreResourceLoader(JNIEnv * env,jobject jLoadListener)71 WebCoreResourceLoader::WebCoreResourceLoader(JNIEnv *env, jobject jLoadListener)
72 {
73 mJLoader = env->NewGlobalRef(jLoadListener);
74 }
75
~WebCoreResourceLoader()76 WebCoreResourceLoader::~WebCoreResourceLoader()
77 {
78 JNIEnv* env = JSC::Bindings::getJNIEnv();
79 SET_NATIVE_HANDLE(env, mJLoader, 0);
80 env->DeleteGlobalRef(mJLoader);
81 mJLoader = 0;
82 }
83
cancel()84 void WebCoreResourceLoader::cancel()
85 {
86 JNIEnv* env = JSC::Bindings::getJNIEnv();
87 env->CallVoidMethod(mJLoader, gResourceLoader.mCancelMethodID);
88 checkException(env);
89 }
90
downloadFile()91 void WebCoreResourceLoader::downloadFile()
92 {
93 JNIEnv* env = JSC::Bindings::getJNIEnv();
94 env->CallVoidMethod(mJLoader, gResourceLoader.mDownloadFileMethodID);
95 checkException(env);
96 }
97
98 /*
99 * This static method is called to check to see if a POST response is in
100 * the cache. This may be slow, but is only used during a navigation to
101 * a POST response.
102 */
willLoadFromCache(const WebCore::KURL & url)103 bool WebCoreResourceLoader::willLoadFromCache(const WebCore::KURL& url)
104 {
105 JNIEnv* env = JSC::Bindings::getJNIEnv();
106 WebCore::String urlStr = url.string();
107 jstring jUrlStr = env->NewString(urlStr.characters(), urlStr.length());
108 jclass resourceLoader = env->FindClass("android/webkit/LoadListener");
109 bool val = env->CallStaticBooleanMethod(resourceLoader,
110 gResourceLoader.mWillLoadFromCacheMethodID, jUrlStr);
111 checkException(env);
112 env->DeleteLocalRef(jUrlStr);
113
114 return val;
115 }
116
117 // ----------------------------------------------------------------------------
SetResponseHeader(JNIEnv * env,jobject obj,jint nativeResponse,jstring key,jstring val)118 void WebCoreResourceLoader::SetResponseHeader(JNIEnv* env, jobject obj, jint nativeResponse, jstring key, jstring val)
119 {
120 #ifdef ANDROID_INSTRUMENT
121 TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
122 #endif
123
124 WebCore::ResourceResponse* response = (WebCore::ResourceResponse*)nativeResponse;
125 LOG_ASSERT(response, "nativeSetResponseHeader must take a valid response pointer!");
126
127 LOG_ASSERT(key, "How did a null value become a key?");
128 if (val) {
129 WebCore::String valStr = to_string(env, val);
130 if (!valStr.isEmpty())
131 response->setHTTPHeaderField(to_string(env, key), valStr);
132 }
133 }
134
CreateResponse(JNIEnv * env,jobject obj,jstring url,jint statusCode,jstring statusText,jstring mimeType,jlong expectedLength,jstring encoding)135 jint WebCoreResourceLoader::CreateResponse(JNIEnv* env, jobject obj, jstring url, jint statusCode,
136 jstring statusText, jstring mimeType, jlong expectedLength,
137 jstring encoding)
138 {
139 #ifdef ANDROID_INSTRUMENT
140 TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
141 #endif
142 LOG_ASSERT(url, "Must have a url in the response!");
143 WebCore::KURL kurl(to_string(env, url));
144 WebCore::String encodingStr;
145 WebCore::String mimeTypeStr;
146 if (mimeType) {
147 mimeTypeStr = to_string(env, mimeType);
148 LOGV("Response setMIMEType: %s", mimeTypeStr.latin1().data());
149 }
150 if (encoding) {
151 encodingStr = to_string(env, encoding);
152 LOGV("Response setTextEncodingName: %s", encodingStr.latin1().data());
153 }
154 WebCore::ResourceResponse* response = new WebCore::ResourceResponse(
155 kurl, mimeTypeStr, (long long)expectedLength,
156 encodingStr, WebCore::String());
157 response->setHTTPStatusCode(statusCode);
158 if (statusText) {
159 WebCore::String status = to_string(env, statusText);
160 response->setHTTPStatusText(status);
161 LOGV("Response setStatusText: %s", status.latin1().data());
162 }
163 return (int)response;
164 }
165
ReceivedResponse(JNIEnv * env,jobject obj,jint nativeResponse)166 void WebCoreResourceLoader::ReceivedResponse(JNIEnv* env, jobject obj, jint nativeResponse)
167 {
168 #ifdef ANDROID_INSTRUMENT
169 TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
170 #endif
171 WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
172 LOG_ASSERT(handle, "nativeReceivedResponse must take a valid handle!");
173 // ResourceLoader::didFail() can set handle to be NULL, we need to check
174 if (!handle)
175 return;
176
177 WebCore::ResourceResponse* response = (WebCore::ResourceResponse*)nativeResponse;
178 LOG_ASSERT(response, "nativeReceivedResponse must take a valid resource pointer!");
179 handle->client()->didReceiveResponse(handle, *response);
180 // As the client makes a copy of the response, delete it here.
181 delete response;
182 }
183
AddData(JNIEnv * env,jobject obj,jbyteArray dataArray,jint length)184 void WebCoreResourceLoader::AddData(JNIEnv* env, jobject obj, jbyteArray dataArray, jint length)
185 {
186 #ifdef ANDROID_INSTRUMENT
187 TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
188 #endif
189 LOGV("webcore_resourceloader data(%d)", length);
190
191 WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
192 LOG_ASSERT(handle, "nativeAddData must take a valid handle!");
193 // ResourceLoader::didFail() can set handle to be NULL, we need to check
194 if (!handle)
195 return;
196
197 SkAutoMemoryUsageProbe mup("android_webcore_resourceloader_nativeAddData");
198
199 bool result = false;
200 jbyte * data = env->GetByteArrayElements(dataArray, NULL);
201
202 LOG_ASSERT(handle->client(), "Why do we not have a client?");
203 handle->client()->didReceiveData(handle, (const char *)data, length, length);
204 env->ReleaseByteArrayElements(dataArray, data, JNI_ABORT);
205 }
206
Finished(JNIEnv * env,jobject obj)207 void WebCoreResourceLoader::Finished(JNIEnv* env, jobject obj)
208 {
209 #ifdef ANDROID_INSTRUMENT
210 TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
211 #endif
212 LOGV("webcore_resourceloader finished");
213 WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
214 LOG_ASSERT(handle, "nativeFinished must take a valid handle!");
215 // ResourceLoader::didFail() can set handle to be NULL, we need to check
216 if (!handle)
217 return;
218
219 LOG_ASSERT(handle->client(), "Why do we not have a client?");
220 handle->client()->didFinishLoading(handle);
221 }
222
RedirectedToUrl(JNIEnv * env,jobject obj,jstring baseUrl,jstring redirectTo,jint nativeResponse)223 jstring WebCoreResourceLoader::RedirectedToUrl(JNIEnv* env, jobject obj,
224 jstring baseUrl, jstring redirectTo, jint nativeResponse)
225 {
226 #ifdef ANDROID_INSTRUMENT
227 TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
228 #endif
229 LOGV("webcore_resourceloader redirectedToUrl");
230 WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
231 LOG_ASSERT(handle, "nativeRedirectedToUrl must take a valid handle!");
232 // ResourceLoader::didFail() can set handle to be NULL, we need to check
233 if (!handle)
234 return NULL;
235
236 LOG_ASSERT(handle->client(), "Why do we not have a client?");
237 WebCore::ResourceRequest r = handle->request();
238 WebCore::KURL url(WebCore::KURL(to_string(env, baseUrl)),
239 to_string(env, redirectTo));
240 r.setURL(url);
241 if (r.httpMethod() == "POST") {
242 r.setHTTPMethod("GET");
243 r.clearHTTPReferrer();
244 r.setHTTPBody(0);
245 r.setHTTPContentType("");
246 }
247 WebCore::ResourceResponse* response = (WebCore::ResourceResponse*)nativeResponse;
248 // If the url fails to resolve the relative path, return null.
249 if (url.protocol().isEmpty()) {
250 delete response;
251 return NULL;
252 }
253 handle->client()->willSendRequest(handle, r, *response);
254 delete response;
255 WebCore::String s = url.string();
256 return env->NewString((unsigned short*)s.characters(), s.length());
257 }
258
Error(JNIEnv * env,jobject obj,jint id,jstring description,jstring failingUrl)259 void WebCoreResourceLoader::Error(JNIEnv* env, jobject obj, jint id, jstring description,
260 jstring failingUrl)
261 {
262 #ifdef ANDROID_INSTRUMENT
263 TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
264 #endif
265 LOGV("webcore_resourceloader error");
266 WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
267 LOG_ASSERT(handle, "nativeError must take a valid handle!");
268 // ResourceLoader::didFail() can set handle to be NULL, we need to check
269 if (!handle)
270 return;
271
272 handle->client()->didFail(handle, WebCore::ResourceError("", id,
273 to_string(env, failingUrl), to_string(env, description)));
274 }
275
276 // ----------------------------------------------------------------------------
277
278 /*
279 * JNI registration.
280 */
281 static JNINativeMethod gResourceloaderMethods[] = {
282 /* name, signature, funcPtr */
283 { "nativeSetResponseHeader", "(ILjava/lang/String;Ljava/lang/String;)V",
284 (void*) WebCoreResourceLoader::SetResponseHeader },
285 { "nativeCreateResponse", "(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JLjava/lang/String;)I",
286 (void*) WebCoreResourceLoader::CreateResponse },
287 { "nativeReceivedResponse", "(I)V",
288 (void*) WebCoreResourceLoader::ReceivedResponse },
289 { "nativeAddData", "([BI)V",
290 (void*) WebCoreResourceLoader::AddData },
291 { "nativeFinished", "()V",
292 (void*) WebCoreResourceLoader::Finished },
293 { "nativeRedirectedToUrl", "(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;",
294 (void*) WebCoreResourceLoader::RedirectedToUrl },
295 { "nativeError", "(ILjava/lang/String;Ljava/lang/String;)V",
296 (void*) WebCoreResourceLoader::Error }
297 };
298
register_resource_loader(JNIEnv * env)299 int register_resource_loader(JNIEnv* env)
300 {
301 jclass resourceLoader = env->FindClass("android/webkit/LoadListener");
302 LOG_FATAL_IF(resourceLoader == NULL,
303 "Unable to find class android/webkit/LoadListener");
304
305 gResourceLoader.mObject =
306 env->GetFieldID(resourceLoader, "mNativeLoader", "I");
307 LOG_FATAL_IF(gResourceLoader.mObject == NULL,
308 "Unable to find android/webkit/LoadListener.mNativeLoader");
309
310 gResourceLoader.mCancelMethodID =
311 env->GetMethodID(resourceLoader, "cancel", "()V");
312 LOG_FATAL_IF(gResourceLoader.mCancelMethodID == NULL,
313 "Could not find method cancel on LoadListener");
314
315 gResourceLoader.mDownloadFileMethodID =
316 env->GetMethodID(resourceLoader, "downloadFile", "()V");
317 LOG_FATAL_IF(gResourceLoader.mDownloadFileMethodID == NULL,
318 "Could not find method downloadFile on LoadListener");
319
320 gResourceLoader.mWillLoadFromCacheMethodID =
321 env->GetStaticMethodID(resourceLoader, "willLoadFromCache", "(Ljava/lang/String;)Z");
322 LOG_FATAL_IF(gResourceLoader.mWillLoadFromCacheMethodID == NULL,
323 "Could not find static method willLoadFromCache on LoadListener");
324
325 return jniRegisterNativeMethods(env, "android/webkit/LoadListener",
326 gResourceloaderMethods, NELEM(gResourceloaderMethods));
327 }
328
329 } /* namespace android */
330