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