1 /*
2 * Copyright 2010, 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 "UrlInterceptResponse"
27 #include "config.h"
28
29 #include "JNIUtility.h"
30 #include "UrlInterceptResponse.h"
31 #include "WebCoreJni.h"
32
33 #include <utils/Log.h>
34
35 namespace android {
36
37 class JavaInputStreamWrapper {
38 public:
JavaInputStreamWrapper(JNIEnv * env,jobject inputStream)39 JavaInputStreamWrapper(JNIEnv* env, jobject inputStream)
40 : m_inputStream(env->NewGlobalRef(inputStream))
41 , m_buffer(0) {
42 LOG_ALWAYS_FATAL_IF(!inputStream);
43 jclass inputStreamClass = env->FindClass("java/io/InputStream");
44 LOG_ALWAYS_FATAL_IF(!inputStreamClass);
45 m_read = env->GetMethodID(inputStreamClass, "read", "([B)I");
46 LOG_ALWAYS_FATAL_IF(!m_read);
47 m_close = env->GetMethodID(inputStreamClass, "close", "()V");
48 LOG_ALWAYS_FATAL_IF(!m_close);
49 env->DeleteLocalRef(inputStreamClass);
50 }
51
~JavaInputStreamWrapper()52 ~JavaInputStreamWrapper() {
53 JNIEnv* env = JSC::Bindings::getJNIEnv();
54 env->CallVoidMethod(m_inputStream, m_close);
55 checkException(env);
56 env->DeleteGlobalRef(m_inputStream);
57 // In case we never call read().
58 if (m_buffer)
59 env->DeleteGlobalRef(m_buffer);
60 }
61
read(std::vector<char> * out)62 void read(std::vector<char>* out) {
63 JNIEnv* env = JSC::Bindings::getJNIEnv();
64 // Initialize our read buffer to the capacity of out.
65 if (!m_buffer) {
66 m_buffer = env->NewByteArray(out->capacity());
67 m_buffer = (jbyteArray) env->NewGlobalRef(m_buffer);
68 }
69 int size = (int) env->CallIntMethod(m_inputStream, m_read, m_buffer);
70 if (checkException(env) || size < 0)
71 return;
72 // Copy from m_buffer to out.
73 out->resize(size);
74 env->GetByteArrayRegion(m_buffer, 0, size, (jbyte*)&out->front());
75 }
76
77 private:
78 jobject m_inputStream;
79 jbyteArray m_buffer;
80 jmethodID m_read;
81 jmethodID m_close;
82 };
83
UrlInterceptResponse(JNIEnv * env,jobject response)84 UrlInterceptResponse::UrlInterceptResponse(JNIEnv* env, jobject response) {
85 jclass javaResponse = env->FindClass("android/webkit/WebResourceResponse");
86 LOG_ALWAYS_FATAL_IF(!javaResponse);
87 jfieldID mimeType = env->GetFieldID(javaResponse, "mMimeType",
88 "Ljava/lang/String;");
89 LOG_ALWAYS_FATAL_IF(!mimeType);
90 jfieldID encoding = env->GetFieldID(javaResponse, "mEncoding",
91 "Ljava/lang/String;");
92 LOG_ALWAYS_FATAL_IF(!encoding);
93 jfieldID inputStream = env->GetFieldID(javaResponse, "mInputStream",
94 "Ljava/io/InputStream;");
95 LOG_ALWAYS_FATAL_IF(!inputStream);
96
97 jobject stream = env->GetObjectField(response, inputStream);
98 if (stream)
99 m_inputStream.set(new JavaInputStreamWrapper(env, stream));
100
101 jstring mimeStr = (jstring) env->GetObjectField(response, mimeType);
102 jstring encodingStr = (jstring) env->GetObjectField(response, encoding);
103
104 if (mimeStr) {
105 const char* s = env->GetStringUTFChars(mimeStr, NULL);
106 m_mimeType.assign(s, env->GetStringUTFLength(mimeStr));
107 env->ReleaseStringUTFChars(mimeStr, s);
108 }
109 if (encodingStr) {
110 const char* s = env->GetStringUTFChars(encodingStr, NULL);
111 m_encoding.assign(s, env->GetStringUTFLength(encodingStr));
112 env->ReleaseStringUTFChars(encodingStr, s);
113 }
114
115 env->DeleteLocalRef(javaResponse);
116 env->DeleteLocalRef(stream);
117 env->DeleteLocalRef(mimeStr);
118 env->DeleteLocalRef(encodingStr);
119 }
120
~UrlInterceptResponse()121 UrlInterceptResponse::~UrlInterceptResponse() {
122 // Cannot be inlined because of JavaInputStreamWrapper visibility.
123 }
124
readStream(std::vector<char> * out) const125 bool UrlInterceptResponse::readStream(std::vector<char>* out) const {
126 if (!m_inputStream)
127 return false;
128 m_inputStream->read(out);
129 return true;
130 }
131
132 } // namespace android
133