1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/history/android/sqlite_cursor.h"
6
7#include "base/android/jni_android.h"
8#include "base/android/jni_array.h"
9#include "base/android/jni_string.h"
10#include "base/bind.h"
11#include "base/logging.h"
12#include "chrome/browser/favicon/favicon_service.h"
13#include "components/history/core/android/android_history_types.h"
14#include "content/public/browser/browser_thread.h"
15#include "jni/SQLiteCursor_jni.h"
16#include "sql/statement.h"
17
18using base::android::ConvertUTF8ToJavaString;
19using base::android::ScopedJavaLocalRef;
20using content::BrowserThread;
21
22namespace {
23
24SQLiteCursor::JavaColumnType ToJavaColumnType(sql::ColType type) {
25  switch (type) {
26    case sql::COLUMN_TYPE_INTEGER:
27      return SQLiteCursor::NUMERIC;
28    case sql::COLUMN_TYPE_FLOAT:
29      return SQLiteCursor::DOUBLE;
30    case sql::COLUMN_TYPE_TEXT:
31      return SQLiteCursor::LONG_VAR_CHAR;
32    case sql::COLUMN_TYPE_BLOB:
33      return SQLiteCursor::BLOB;
34    case sql::COLUMN_TYPE_NULL:
35      return SQLiteCursor::NULL_TYPE;
36    default:
37      NOTREACHED();
38  }
39  return SQLiteCursor::NULL_TYPE;
40}
41
42}  // namespace.
43
44
45SQLiteCursor::TestObserver::TestObserver() {
46}
47
48SQLiteCursor::TestObserver::~TestObserver() {
49}
50
51ScopedJavaLocalRef<jobject> SQLiteCursor::NewJavaSqliteCursor(
52    JNIEnv* env,
53    const std::vector<std::string>& column_names,
54    history::AndroidStatement* statement,
55    AndroidHistoryProviderService* service,
56    FaviconService* favicon_service) {
57  SQLiteCursor* cursor = new SQLiteCursor(column_names, statement, service,
58                                          favicon_service);
59  return Java_SQLiteCursor_create(env, reinterpret_cast<intptr_t>(cursor));
60}
61
62bool SQLiteCursor::RegisterSqliteCursor(JNIEnv* env) {
63  return RegisterNativesImpl(env);
64}
65
66jint SQLiteCursor::GetCount(JNIEnv* env, jobject obj) {
67  // Moves to maxium possible position so we will reach the last row, then finds
68  // out the total number of rows.
69  int current_position = position_;
70  int count = MoveTo(env, obj, std::numeric_limits<int>::max() - 1) + 1;
71  // Moves back to the previous position.
72  MoveTo(env, obj, current_position);
73  return count;
74}
75
76ScopedJavaLocalRef<jobjectArray> SQLiteCursor::GetColumnNames(JNIEnv* env,
77                                                              jobject obj) {
78  return base::android::ToJavaArrayOfStrings(env, column_names_);
79}
80
81ScopedJavaLocalRef<jstring> SQLiteCursor::GetString(JNIEnv* env,
82                                                    jobject obj,
83                                                    jint column) {
84  base::string16 value = statement_->statement()->ColumnString16(column);
85  return ScopedJavaLocalRef<jstring>(env,
86      env->NewString(value.data(), value.size()));
87}
88
89jlong SQLiteCursor::GetLong(JNIEnv* env, jobject obj, jint column) {
90  return statement_->statement()->ColumnInt64(column);
91}
92
93jint SQLiteCursor::GetInt(JNIEnv* env, jobject obj, jint column) {
94  return statement_->statement()->ColumnInt(column);
95}
96
97jdouble SQLiteCursor::GetDouble(JNIEnv* env, jobject obj, jint column) {
98  return statement_->statement()->ColumnDouble(column);
99}
100
101ScopedJavaLocalRef<jbyteArray> SQLiteCursor::GetBlob(JNIEnv* env,
102                                                     jobject obj,
103                                                     jint column) {
104  std::vector<unsigned char> blob;
105
106  // Assume the client will only get favicon using GetBlob.
107  if (statement_->favicon_index() == column) {
108    if (!GetFavicon(statement_->statement()->ColumnInt(column), &blob))
109      return ScopedJavaLocalRef<jbyteArray>();
110  } else {
111    statement_->statement()->ColumnBlobAsVector(column, &blob);
112  }
113  return base::android::ToJavaByteArray(env, &blob[0], blob.size());
114}
115
116jboolean SQLiteCursor::IsNull(JNIEnv* env, jobject obj, jint column) {
117  return NULL_TYPE == GetColumnTypeInternal(column) ? JNI_TRUE : JNI_FALSE;
118}
119
120jint SQLiteCursor::MoveTo(JNIEnv* env, jobject obj, jint pos) {
121  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
122      base::Bind(&SQLiteCursor::RunMoveStatementOnUIThread,
123      base::Unretained(this), pos));
124  if (test_observer_)
125    test_observer_->OnPostMoveToTask();
126
127  event_.Wait();
128  return position_;
129}
130
131jint SQLiteCursor::GetColumnType(JNIEnv* env, jobject obj, jint column) {
132  return GetColumnTypeInternal(column);
133}
134
135void SQLiteCursor::Destroy(JNIEnv* env, jobject obj) {
136  // We do our best to cleanup when Destroy() is called from Java's finalize()
137  // where the UI message loop might stop running or in the process of shutting
138  // down, as the whole process will be destroyed soon, it's fine to leave some
139  // objects out there.
140  if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
141    DestroyOnUIThread();
142  } else if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
143                 base::Bind(&SQLiteCursor::DestroyOnUIThread,
144                     base::Unretained(this)))) {
145    delete this;
146  }
147}
148
149SQLiteCursor::SQLiteCursor(const std::vector<std::string>& column_names,
150                           history::AndroidStatement* statement,
151                           AndroidHistoryProviderService* service,
152                           FaviconService* favicon_service)
153    : position_(-1),
154      event_(false, false),
155      statement_(statement),
156      column_names_(column_names),
157      service_(service),
158      favicon_service_(favicon_service),
159      count_(-1),
160      test_observer_(NULL) {
161}
162
163SQLiteCursor::~SQLiteCursor() {
164}
165
166void SQLiteCursor::DestroyOnUIThread() {
167  // Consumer requests were set in the UI thread. They must be cancelled
168  // using the same thread.
169  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
170  tracker_.reset();
171  service_->CloseStatement(statement_);
172  delete this;
173}
174
175bool SQLiteCursor::GetFavicon(favicon_base::FaviconID id,
176                              std::vector<unsigned char>* image_data) {
177  if (id) {
178    BrowserThread::PostTask(
179        BrowserThread::UI,
180        FROM_HERE,
181        base::Bind(&SQLiteCursor::GetFaviconForIDInUIThread,
182                   base::Unretained(this), id,
183                   base::Bind(&SQLiteCursor::OnFaviconData,
184                              base::Unretained(this))));
185
186    if (test_observer_)
187      test_observer_->OnPostGetFaviconTask();
188
189    event_.Wait();
190    if (!favicon_bitmap_result_.is_valid())
191      return false;
192
193    scoped_refptr<base::RefCountedMemory> bitmap_data =
194        favicon_bitmap_result_.bitmap_data;
195    image_data->assign(bitmap_data->front(),
196                       bitmap_data->front() + bitmap_data->size());
197    return true;
198  }
199
200  return false;
201}
202
203void SQLiteCursor::GetFaviconForIDInUIThread(
204    favicon_base::FaviconID id,
205    const favicon_base::FaviconRawBitmapCallback& callback) {
206  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
207  if (!tracker_.get())
208    tracker_.reset(new base::CancelableTaskTracker());
209  favicon_service_->GetLargestRawFaviconForID(id, callback, tracker_.get());
210}
211
212void SQLiteCursor::OnFaviconData(
213    const favicon_base::FaviconRawBitmapResult& bitmap_result) {
214  favicon_bitmap_result_ = bitmap_result;
215  event_.Signal();
216  if (test_observer_)
217    test_observer_->OnGetFaviconResult();
218}
219
220void SQLiteCursor::OnMoved(int pos) {
221  position_ = pos;
222  event_.Signal();
223  if (test_observer_)
224    // Notified test_observer on UI thread instead of the one it will wait.
225    test_observer_->OnGetMoveToResult();
226}
227
228SQLiteCursor::JavaColumnType SQLiteCursor::GetColumnTypeInternal(int column) {
229  if (column == statement_->favicon_index())
230    return SQLiteCursor::BLOB;
231
232  return ToJavaColumnType(statement_->statement()->ColumnType(column));
233}
234
235void SQLiteCursor::RunMoveStatementOnUIThread(int pos) {
236  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
237  if (!tracker_.get())
238    tracker_.reset(new base::CancelableTaskTracker());
239  service_->MoveStatement(
240      statement_,
241      position_,
242      pos,
243      base::Bind(&SQLiteCursor::OnMoved, base::Unretained(this)),
244      tracker_.get());
245}
246