sqlite_cursor.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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/history/android/android_history_types.h"
13#include "content/public/browser/browser_thread.h"
14#include "jni/SQLiteCursor_jni.h"
15#include "sql/statement.h"
16
17using base::android::ConvertUTF8ToJavaString;
18using base::android::ScopedJavaLocalRef;
19using content::BrowserThread;
20
21namespace {
22
23SQLiteCursor::JavaColumnType ToJavaColumnType(sql::ColType type) {
24  switch (type) {
25    case sql::COLUMN_TYPE_INTEGER:
26      return SQLiteCursor::NUMERIC;
27    case sql::COLUMN_TYPE_FLOAT:
28      return SQLiteCursor::DOUBLE;
29    case sql::COLUMN_TYPE_TEXT:
30      return SQLiteCursor::LONG_VAR_CHAR;
31    case sql::COLUMN_TYPE_BLOB:
32      return SQLiteCursor::BLOB;
33    case sql::COLUMN_TYPE_NULL:
34      return SQLiteCursor::NULL_TYPE;
35    default:
36      NOTREACHED();
37  }
38  return SQLiteCursor::NULL_TYPE;
39}
40
41}  // namespace.
42
43
44SQLiteCursor::TestObserver::TestObserver() {
45}
46
47SQLiteCursor::TestObserver::~TestObserver() {
48}
49
50ScopedJavaLocalRef<jobject> SQLiteCursor::NewJavaSqliteCursor(
51    JNIEnv* env,
52    const std::vector<std::string>& column_names,
53    history::AndroidStatement* statement,
54    AndroidHistoryProviderService* service,
55    FaviconService* favicon_service) {
56  SQLiteCursor* cursor = new SQLiteCursor(column_names, statement, service,
57                                          favicon_service);
58  return Java_SQLiteCursor_create(env, reinterpret_cast<intptr_t>(cursor));
59}
60
61bool SQLiteCursor::RegisterSqliteCursor(JNIEnv* env) {
62  return RegisterNativesImpl(env);
63}
64
65jint SQLiteCursor::GetCount(JNIEnv* env, jobject obj) {
66  // Moves to maxium possible position so we will reach the last row, then finds
67  // out the total number of rows.
68  int current_position = position_;
69  int count = MoveTo(env, obj, std::numeric_limits<int>::max() - 1) + 1;
70  // Moves back to the previous position.
71  MoveTo(env, obj, current_position);
72  return count;
73}
74
75ScopedJavaLocalRef<jobjectArray> SQLiteCursor::GetColumnNames(JNIEnv* env,
76                                                              jobject obj) {
77  return base::android::ToJavaArrayOfStrings(env, column_names_);
78}
79
80ScopedJavaLocalRef<jstring> SQLiteCursor::GetString(JNIEnv* env,
81                                                    jobject obj,
82                                                    jint column) {
83  base::string16 value = statement_->statement()->ColumnString16(column);
84  return ScopedJavaLocalRef<jstring>(env,
85      env->NewString(value.data(), value.size()));
86}
87
88jlong SQLiteCursor::GetLong(JNIEnv* env, jobject obj, jint column) {
89  return statement_->statement()->ColumnInt64(column);
90}
91
92jint SQLiteCursor::GetInt(JNIEnv* env, jobject obj, jint column) {
93  return statement_->statement()->ColumnInt(column);
94}
95
96jdouble SQLiteCursor::GetDouble(JNIEnv* env, jobject obj, jint column) {
97  return statement_->statement()->ColumnDouble(column);
98}
99
100ScopedJavaLocalRef<jbyteArray> SQLiteCursor::GetBlob(JNIEnv* env,
101                                                     jobject obj,
102                                                     jint column) {
103  std::vector<unsigned char> blob;
104
105  // Assume the client will only get favicon using GetBlob.
106  if (statement_->favicon_index() == column) {
107    if (!GetFavicon(statement_->statement()->ColumnInt(column), &blob))
108      return ScopedJavaLocalRef<jbyteArray>();
109  } else {
110    statement_->statement()->ColumnBlobAsVector(column, &blob);
111  }
112  return base::android::ToJavaByteArray(env, &blob[0], blob.size());
113}
114
115jboolean SQLiteCursor::IsNull(JNIEnv* env, jobject obj, jint column) {
116  return NULL_TYPE == GetColumnTypeInternal(column) ? JNI_TRUE : JNI_FALSE;
117}
118
119jint SQLiteCursor::MoveTo(JNIEnv* env, jobject obj, jint pos) {
120  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
121      base::Bind(&SQLiteCursor::RunMoveStatementOnUIThread,
122      base::Unretained(this), pos));
123  if (test_observer_)
124    test_observer_->OnPostMoveToTask();
125
126  event_.Wait();
127  return position_;
128}
129
130jint SQLiteCursor::GetColumnType(JNIEnv* env, jobject obj, jint column) {
131  return GetColumnTypeInternal(column);
132}
133
134void SQLiteCursor::Destroy(JNIEnv* env, jobject obj) {
135  // We do our best to cleanup when Destroy() is called from Java's finalize()
136  // where the UI message loop might stop running or in the process of shutting
137  // down, as the whole process will be destroyed soon, it's fine to leave some
138  // objects out there.
139  if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
140    DestroyOnUIThread();
141  } else if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
142                 base::Bind(&SQLiteCursor::DestroyOnUIThread,
143                     base::Unretained(this)))) {
144    delete this;
145  }
146}
147
148SQLiteCursor::SQLiteCursor(const std::vector<std::string>& column_names,
149                           history::AndroidStatement* statement,
150                           AndroidHistoryProviderService* service,
151                           FaviconService* favicon_service)
152    : position_(-1),
153      event_(false, false),
154      statement_(statement),
155      column_names_(column_names),
156      service_(service),
157      favicon_service_(favicon_service),
158      count_(-1),
159      test_observer_(NULL) {
160}
161
162SQLiteCursor::~SQLiteCursor() {
163}
164
165void SQLiteCursor::DestroyOnUIThread() {
166  // Consumer requests were set in the UI thread. They must be cancelled
167  // using the same thread.
168  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
169  consumer_.reset();
170  tracker_.reset();
171  service_->CloseStatement(statement_);
172  delete this;
173}
174
175bool SQLiteCursor::GetFavicon(chrome::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    chrome::FaviconID id,
205    const FaviconService::FaviconRawCallback& callback) {
206  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
207  if (!tracker_.get())
208    tracker_.reset(new CancelableTaskTracker());
209  favicon_service_->GetLargestRawFaviconForID(id, callback, tracker_.get());
210}
211
212
213void SQLiteCursor::OnFaviconData(
214    const chrome::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