summaryrefslogtreecommitdiffstats
path: root/android_webview/native/input_stream_impl.cc
blob: 515cb51cedeea4b0eca25ec97804ed89863b1300 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "android_webview/native/input_stream_impl.h"

#include "base/android/jni_android.h"
// Disable "Warnings treated as errors" for input_stream_jni as it's a Java
// system class and we have to generate C++ hooks for all methods in the class
// even if they're unused.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni/InputStream_jni.h"
#pragma GCC diagnostic pop
#include "net/base/io_buffer.h"

using base::android::AttachCurrentThread;
using base::android::ClearException;
using base::android::JavaRef;
using JNI_InputStream::Java_InputStream_available;
using JNI_InputStream::Java_InputStream_close;
using JNI_InputStream::Java_InputStream_skip;
using JNI_InputStream::Java_InputStream_readI_AB_I_I;

namespace android_webview {

bool RegisterInputStream(JNIEnv* env) {
  return JNI_InputStream::RegisterNativesImpl(env);
}

// Maximum number of bytes to be read in a single read.
const int InputStreamImpl::kBufferSize = 4096;

//static
const InputStreamImpl* InputStreamImpl::FromInputStream(
        const InputStream* input_stream) {
    return static_cast<const InputStreamImpl*>(input_stream);
}

// TODO: Use unsafe version for all Java_InputStream methods in this file
// once BUG 157880 is fixed and implement graceful exception handling.

InputStreamImpl::InputStreamImpl() {
}

InputStreamImpl::InputStreamImpl(const JavaRef<jobject>& stream)
    : jobject_(stream) {
  DCHECK(!stream.is_null());
}

InputStreamImpl::~InputStreamImpl() {
  JNIEnv* env = AttachCurrentThread();
  Java_InputStream_close(env, jobject_.obj());
}

bool InputStreamImpl::BytesAvailable(int* bytes_available) const {
  JNIEnv* env = AttachCurrentThread();
  int bytes = Java_InputStream_available(env, jobject_.obj());
  if (ClearException(env))
    return false;
  *bytes_available = bytes;
  return true;
}

bool InputStreamImpl::Skip(int64_t n, int64_t* bytes_skipped) {
  JNIEnv* env = AttachCurrentThread();
  int bytes = Java_InputStream_skip(env, jobject_.obj(), n);
  if (ClearException(env))
    return false;
  if (bytes > n)
    return false;
  *bytes_skipped = bytes;
  return true;
}

bool InputStreamImpl::Read(net::IOBuffer* dest, int length, int* bytes_read) {
  JNIEnv* env = AttachCurrentThread();
  if (!buffer_.obj()) {
    // Allocate transfer buffer.
    buffer_.Reset(env, env->NewByteArray(kBufferSize));
    if (ClearException(env))
      return false;
  }

  jbyteArray buffer = buffer_.obj();
  *bytes_read = 0;

  const int read_size = std::min(length, kBufferSize);
  int32_t byte_count;
  do {
    // Unfortunately it is valid for the Java InputStream to read 0 bytes some
    // number of times before returning any more data. Because this method
    // signals EOF by setting |bytes_read| to 0 and returning true necessary to
    // call the Java-side read method until it returns something other than 0.
    byte_count = Java_InputStream_readI_AB_I_I(
        env, jobject_.obj(), buffer, 0, read_size);
    if (ClearException(env))
      return false;
  } while (byte_count == 0);

  // We've reached the end of the stream.
  if (byte_count < 0)
    return true;

#ifndef NDEBUG
  int32_t buffer_length = env->GetArrayLength(buffer);
  DCHECK_GE(read_size, byte_count);
  DCHECK_GE(buffer_length, byte_count);
#endif // NDEBUG

  // The DCHECKs are in place to help Chromium developers in case of bugs,
  // this check is to prevent a malicious InputStream implementation from
  // overrunning the |dest| buffer.
  if (byte_count > read_size)
    return false;

  // Copy the data over to the provided C++ side buffer.
  DCHECK_GE(length, byte_count);
  env->GetByteArrayRegion(buffer, 0, byte_count,
      reinterpret_cast<jbyte*>(dest->data() + *bytes_read));
  if (ClearException(env))
    return false;

  *bytes_read = byte_count;
  return true;
}

} // namespace android_webview