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