sqlite_cursor.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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 "chrome/browser/history/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  consumer_.reset();
171  tracker_.reset();
172  service_->CloseStatement(statement_);
173  delete this;
174}
175
176bool SQLiteCursor::GetFavicon(favicon_base::FaviconID id,
177                              std::vector<unsigned char>* image_data) {
178  if (id) {
179    BrowserThread::PostTask(
180        BrowserThread::UI,
181        FROM_HERE,
182        base::Bind(&SQLiteCursor::GetFaviconForIDInUIThread,
183                   base::Unretained(this), id,
184                   base::Bind(&SQLiteCursor::OnFaviconData,
185                              base::Unretained(this))));
186
187    if (test_observer_)
188      test_observer_->OnPostGetFaviconTask();
189
190    event_.Wait();
191    if (!favicon_bitmap_result_.is_valid())
192      return false;
193
194    scoped_refptr<base::RefCountedMemory> bitmap_data =
195        favicon_bitmap_result_.bitmap_data;
196    image_data->assign(bitmap_data->front(),
197                       bitmap_data->front() + bitmap_data->size());
198    return true;
199  }
200
201  return false;
202}
203
204void SQLiteCursor::GetFaviconForIDInUIThread(
205    favicon_base::FaviconID id,
206    const favicon_base::FaviconRawCallback& callback) {
207  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
208  if (!tracker_.get())
209    tracker_.reset(new base::CancelableTaskTracker());
210  favicon_service_->GetLargestRawFaviconForID(id, callback, tracker_.get());
211}
212
213void SQLiteCursor::OnFaviconData(
214    const favicon_base::FaviconBitmapResult& bitmap_result) {
215  favicon_bitmap_result_ = bitmap_result;
216  event_.Signal();
217  if (test_observer_)
218    test_observer_->OnGetFaviconResult();
219}
220
221void SQLiteCursor::OnMoved(AndroidHistoryProviderService::Handle handle,
222                           int pos) {
223  position_ = pos;
224  event_.Signal();
225  if (test_observer_)
226    // Notified test_observer on UI thread instead of the one it will wait.
227    test_observer_->OnGetMoveToResult();
228}
229
230SQLiteCursor::JavaColumnType SQLiteCursor::GetColumnTypeInternal(int column) {
231  if (column == statement_->favicon_index())
232    return SQLiteCursor::BLOB;
233
234  return ToJavaColumnType(statement_->statement()->ColumnType(column));
235}
236
237void SQLiteCursor::RunMoveStatementOnUIThread(int pos) {
238  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
239  if (!consumer_.get())
240    consumer_.reset(new CancelableRequestConsumer());
241  service_->MoveStatement(
242      statement_, position_, pos, consumer_.get(),
243      base::Bind(&SQLiteCursor::OnMoved, base::Unretained(this)));
244}
245