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 <ScopedLocalRef.h>
34 #include <utils/Log.h>
35
36 namespace android {
37
38 class JavaInputStreamWrapper {
39 public:
JavaInputStreamWrapper(JNIEnv * env,jobject inputStream)40 JavaInputStreamWrapper(JNIEnv* env, jobject inputStream)
41 : m_inputStream(env->NewGlobalRef(inputStream))
42 , m_buffer(NULL) {
43 LOG_ALWAYS_FATAL_IF(!m_inputStream);
44 ScopedLocalRef<jclass> inputStreamClass(env, env->FindClass("java/io/InputStream"));
45 LOG_ALWAYS_FATAL_IF(!inputStreamClass.get());
46 m_read = env->GetMethodID(inputStreamClass.get(), "read", "([B)I");
47 LOG_ALWAYS_FATAL_IF(!m_read);
48 m_close = env->GetMethodID(inputStreamClass.get(), "close", "()V");
49 LOG_ALWAYS_FATAL_IF(!m_close);
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 ScopedLocalRef<jbyteArray> buffer_local(env, env->NewByteArray(out->capacity()));
67 m_buffer = static_cast<jbyteArray>(env->NewGlobalRef(buffer_local.get()));
68 }
69 int size = 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 ScopedLocalRef<jclass> javaResponse(env, env->FindClass("android/webkit/WebResourceResponse"));
86 LOG_ALWAYS_FATAL_IF(!javaResponse.get());
87 jfieldID mimeType = env->GetFieldID(javaResponse.get(), "mMimeType", "Ljava/lang/String;");
88 LOG_ALWAYS_FATAL_IF(!mimeType);
89 jfieldID encoding = env->GetFieldID(javaResponse.get(), "mEncoding", "Ljava/lang/String;");
90 LOG_ALWAYS_FATAL_IF(!encoding);
91 jfieldID inputStream = env->GetFieldID(javaResponse.get(), "mInputStream", "Ljava/io/InputStream;");
92 LOG_ALWAYS_FATAL_IF(!inputStream);
93
94 ScopedLocalRef<jobject> stream(env, env->GetObjectField(response, inputStream));
95 if (stream.get())
96 m_inputStream.set(new JavaInputStreamWrapper(env, stream.get()));
97
98 ScopedLocalRef<jstring> mimeStr(env, static_cast<jstring>(env->GetObjectField(response, mimeType)));
99 ScopedLocalRef<jstring> encodingStr(env, static_cast<jstring>(env->GetObjectField(response, encoding)));
100
101 if (mimeStr.get()) {
102 const char* s = env->GetStringUTFChars(mimeStr.get(), NULL);
103 m_mimeType.assign(s, env->GetStringUTFLength(mimeStr.get()));
104 env->ReleaseStringUTFChars(mimeStr.get(), s);
105 }
106 if (encodingStr.get()) {
107 const char* s = env->GetStringUTFChars(encodingStr.get(), NULL);
108 m_encoding.assign(s, env->GetStringUTFLength(encodingStr.get()));
109 env->ReleaseStringUTFChars(encodingStr.get(), s);
110 }
111 }
112
~UrlInterceptResponse()113 UrlInterceptResponse::~UrlInterceptResponse() {
114 // Cannot be inlined because of JavaInputStreamWrapper visibility.
115 }
116
readStream(std::vector<char> * out) const117 bool UrlInterceptResponse::readStream(std::vector<char>* out) const {
118 if (!m_inputStream)
119 return false;
120 m_inputStream->read(out);
121 return true;
122 }
123
124 } // namespace android
125