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 "CString.h"
     32 #include "ResourceError.h"
     33 #include "ResourceHandle.h"
     34 #include "ResourceHandleClient.h"
     35 #include "ResourceHandleInternal.h"
     36 #include "ResourceResponse.h"
     37 #include "SkUtils.h"
     38 #ifdef ANDROID_INSTRUMENT
     39 #include "TimeCounter.h"
     40 #endif
     41 #include "WebCoreJni.h"
     42 
     43 #include <JNIHelp.h>
     44 #include <JNIUtility.h>
     45 #include <SkTypes.h>
     46 #include <stdlib.h>
     47 #include <utils/misc.h>
     48 #include <wtf/Platform.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     WebCore::String urlStr = url.string();
    123     jstring jUrlStr = env->NewString(urlStr.characters(), urlStr.length());
    124     jclass resourceLoader = env->FindClass("android/webkit/LoadListener");
    125     bool val = env->CallStaticBooleanMethod(resourceLoader,
    126             gResourceLoader.mWillLoadFromCacheMethodID, jUrlStr, identifier);
    127     checkException(env);
    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         WebCore::String valStr = to_string(env, val);
    146         if (!valStr.isEmpty())
    147             response->setHTTPHeaderField(to_string(env, key), valStr);
    148     }
    149 }
    150 
    151 jint WebCoreResourceLoader::CreateResponse(JNIEnv* env, jobject obj, jstring url, jint statusCode,
    152                                                     jstring statusText, jstring mimeType, jlong expectedLength,
    153                                                     jstring encoding)
    154 {
    155 #ifdef ANDROID_INSTRUMENT
    156     TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
    157 #endif
    158     LOG_ASSERT(url, "Must have a url in the response!");
    159     WebCore::KURL kurl(WebCore::ParsedURLString, to_string(env, url));
    160     WebCore::String encodingStr;
    161     WebCore::String mimeTypeStr;
    162     if (mimeType) {
    163         mimeTypeStr = to_string(env, mimeType);
    164         LOGV("Response setMIMEType: %s", mimeTypeStr.latin1().data());
    165     }
    166     if (encoding) {
    167         encodingStr = to_string(env, encoding);
    168         LOGV("Response setTextEncodingName: %s", encodingStr.latin1().data());
    169     }
    170     WebCore::ResourceResponse* response = new WebCore::ResourceResponse(
    171             kurl, mimeTypeStr, (long long)expectedLength,
    172             encodingStr, WebCore::String());
    173     response->setHTTPStatusCode(statusCode);
    174     if (statusText) {
    175         WebCore::String status = to_string(env, statusText);
    176         response->setHTTPStatusText(status);
    177         LOGV("Response setStatusText: %s", status.latin1().data());
    178     }
    179     return (int)response;
    180 }
    181 
    182 void WebCoreResourceLoader::ReceivedResponse(JNIEnv* env, jobject obj, jint nativeResponse)
    183 {
    184 #ifdef ANDROID_INSTRUMENT
    185     TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
    186 #endif
    187     WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
    188     LOG_ASSERT(handle, "nativeReceivedResponse must take a valid handle!");
    189     // ResourceLoader::didFail() can set handle to be NULL, we need to check
    190     if (!handle)
    191         return;
    192 
    193     WebCore::ResourceResponse* response = (WebCore::ResourceResponse*)nativeResponse;
    194     LOG_ASSERT(response, "nativeReceivedResponse must take a valid resource pointer!");
    195     handle->client()->didReceiveResponse(handle, *response);
    196     // As the client makes a copy of the response, delete it here.
    197     delete response;
    198 }
    199 
    200 void WebCoreResourceLoader::AddData(JNIEnv* env, jobject obj, jbyteArray dataArray, jint length)
    201 {
    202 #ifdef ANDROID_INSTRUMENT
    203     TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
    204 #endif
    205     LOGV("webcore_resourceloader data(%d)", length);
    206 
    207     WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
    208     LOG_ASSERT(handle, "nativeAddData must take a valid handle!");
    209     // ResourceLoader::didFail() can set handle to be NULL, we need to check
    210     if (!handle)
    211         return;
    212 
    213     SkAutoMemoryUsageProbe  mup("android_webcore_resourceloader_nativeAddData");
    214 
    215     bool result = false;
    216     jbyte * data =  env->GetByteArrayElements(dataArray, NULL);
    217 
    218     LOG_ASSERT(handle->client(), "Why do we not have a client?");
    219     handle->client()->didReceiveData(handle, (const char *)data, length, length);
    220     env->ReleaseByteArrayElements(dataArray, data, JNI_ABORT);
    221 }
    222 
    223 void WebCoreResourceLoader::Finished(JNIEnv* env, jobject obj)
    224 {
    225 #ifdef ANDROID_INSTRUMENT
    226     TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
    227 #endif
    228     LOGV("webcore_resourceloader finished");
    229     WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
    230     LOG_ASSERT(handle, "nativeFinished must take a valid handle!");
    231     // ResourceLoader::didFail() can set handle to be NULL, we need to check
    232     if (!handle)
    233         return;
    234 
    235     LOG_ASSERT(handle->client(), "Why do we not have a client?");
    236     handle->client()->didFinishLoading(handle);
    237 }
    238 
    239 jstring WebCoreResourceLoader::RedirectedToUrl(JNIEnv* env, jobject obj,
    240         jstring baseUrl, jstring redirectTo, jint nativeResponse)
    241 {
    242 #ifdef ANDROID_INSTRUMENT
    243     TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
    244 #endif
    245     LOGV("webcore_resourceloader redirectedToUrl");
    246     WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
    247     LOG_ASSERT(handle, "nativeRedirectedToUrl must take a valid handle!");
    248     // ResourceLoader::didFail() can set handle to be NULL, we need to check
    249     if (!handle)
    250         return NULL;
    251 
    252     LOG_ASSERT(handle->client(), "Why do we not have a client?");
    253     WebCore::ResourceRequest r = handle->request();
    254     WebCore::KURL url(WebCore::KURL(WebCore::ParsedURLString, to_string(env, baseUrl)),
    255             to_string(env, redirectTo));
    256     WebCore::ResourceResponse* response = (WebCore::ResourceResponse*)nativeResponse;
    257     // If the url fails to resolve the relative path, return null.
    258     if (url.protocol().isEmpty()) {
    259         delete response;
    260         return NULL;
    261     } else {
    262         // Ensure the protocol is lowercase.
    263         url.setProtocol(url.protocol().lower());
    264     }
    265     // Set the url after updating the protocol.
    266     r.setURL(url);
    267     if (r.httpMethod() == "POST") {
    268         r.setHTTPMethod("GET");
    269         r.clearHTTPReferrer();
    270         r.setHTTPBody(0);
    271         r.setHTTPContentType("");
    272     }
    273     handle->client()->willSendRequest(handle, r, *response);
    274     delete response;
    275     WebCore::String s = url.string();
    276     return env->NewString((unsigned short*)s.characters(), s.length());
    277 }
    278 
    279 void WebCoreResourceLoader::Error(JNIEnv* env, jobject obj, jint id, jstring description,
    280         jstring failingUrl)
    281 {
    282 #ifdef ANDROID_INSTRUMENT
    283     TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
    284 #endif
    285     LOGV("webcore_resourceloader error");
    286     WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
    287     LOG_ASSERT(handle, "nativeError must take a valid handle!");
    288     // ResourceLoader::didFail() can set handle to be NULL, we need to check
    289     if (!handle)
    290         return;
    291 
    292     handle->client()->didFail(handle, WebCore::ResourceError("", id,
    293                 to_string(env, failingUrl), to_string(env, description)));
    294 }
    295 
    296 // ----------------------------------------------------------------------------
    297 
    298 /*
    299  * JNI registration.
    300  */
    301 static JNINativeMethod gResourceloaderMethods[] = {
    302     /* name, signature, funcPtr */
    303     { "nativeSetResponseHeader", "(ILjava/lang/String;Ljava/lang/String;)V",
    304         (void*) WebCoreResourceLoader::SetResponseHeader },
    305     { "nativeCreateResponse", "(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JLjava/lang/String;)I",
    306         (void*) WebCoreResourceLoader::CreateResponse },
    307     { "nativeReceivedResponse", "(I)V",
    308         (void*) WebCoreResourceLoader::ReceivedResponse },
    309     { "nativeAddData", "([BI)V",
    310         (void*) WebCoreResourceLoader::AddData },
    311     { "nativeFinished", "()V",
    312         (void*) WebCoreResourceLoader::Finished },
    313     { "nativeRedirectedToUrl", "(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;",
    314         (void*) WebCoreResourceLoader::RedirectedToUrl },
    315     { "nativeError", "(ILjava/lang/String;Ljava/lang/String;)V",
    316         (void*) WebCoreResourceLoader::Error }
    317 };
    318 
    319 int register_resource_loader(JNIEnv* env)
    320 {
    321     jclass resourceLoader = env->FindClass("android/webkit/LoadListener");
    322     LOG_FATAL_IF(resourceLoader == NULL,
    323         "Unable to find class android/webkit/LoadListener");
    324 
    325     gResourceLoader.mObject =
    326         env->GetFieldID(resourceLoader, "mNativeLoader", "I");
    327     LOG_FATAL_IF(gResourceLoader.mObject == NULL,
    328         "Unable to find android/webkit/LoadListener.mNativeLoader");
    329 
    330     gResourceLoader.mCancelMethodID =
    331         env->GetMethodID(resourceLoader, "cancel", "()V");
    332     LOG_FATAL_IF(gResourceLoader.mCancelMethodID == NULL,
    333         "Could not find method cancel on LoadListener");
    334 
    335     gResourceLoader.mDownloadFileMethodID =
    336         env->GetMethodID(resourceLoader, "downloadFile", "()V");
    337     LOG_FATAL_IF(gResourceLoader.mDownloadFileMethodID == NULL,
    338         "Could not find method downloadFile on LoadListener");
    339 
    340     gResourceLoader.mPauseLoadMethodID =
    341         env->GetMethodID(resourceLoader, "pauseLoad", "(Z)V");
    342     LOG_FATAL_IF(gResourceLoader.mPauseLoadMethodID == NULL,
    343         "Could not find method pauseLoad on LoadListener");
    344 
    345     gResourceLoader.mWillLoadFromCacheMethodID =
    346         env->GetStaticMethodID(resourceLoader, "willLoadFromCache", "(Ljava/lang/String;J)Z");
    347     LOG_FATAL_IF(gResourceLoader.mWillLoadFromCacheMethodID == NULL,
    348         "Could not find static method willLoadFromCache on LoadListener");
    349 
    350     return jniRegisterNativeMethods(env, "android/webkit/LoadListener",
    351                      gResourceloaderMethods, NELEM(gResourceloaderMethods));
    352 }
    353 
    354 } /* namespace android */
    355