sqlite_cursor.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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<jint>(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  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  delete this;
136}
137
138SQLiteCursor::SQLiteCursor(const std::vector<std::string>& column_names,
139                           history::AndroidStatement* statement,
140                           AndroidHistoryProviderService* service,
141                           FaviconService* favicon_service)
142    : position_(-1),
143      event_(false, false),
144      statement_(statement),
145      column_names_(column_names),
146      service_(service),
147      favicon_service_(favicon_service),
148      count_(-1),
149      test_observer_(NULL) {
150}
151
152SQLiteCursor::~SQLiteCursor() {
153  // Consumer requests were set in the UI thread. They must be cancelled
154  // using the same thread.
155  if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
156    CancelAllRequests(NULL);
157  } else {
158    base::WaitableEvent event(false, false);
159    BrowserThread::PostTask(
160        BrowserThread::UI,
161        FROM_HERE,
162        base::Bind(&SQLiteCursor::CancelAllRequests, base::Unretained(this),
163                   &event));
164    event.Wait();
165  }
166
167  BrowserThread::PostTask(
168      BrowserThread::UI,
169      FROM_HERE,
170      base::Bind(&AndroidHistoryProviderService::CloseStatement,
171                 base::Unretained(service_), statement_));
172}
173
174bool SQLiteCursor::GetFavicon(chrome::FaviconID id,
175                              std::vector<unsigned char>* image_data) {
176  if (id) {
177    BrowserThread::PostTask(
178        BrowserThread::UI,
179        FROM_HERE,
180        base::Bind(&SQLiteCursor::GetFaviconForIDInUIThread,
181                   base::Unretained(this), id,
182                   base::Bind(&SQLiteCursor::OnFaviconData,
183                              base::Unretained(this))));
184
185    if (test_observer_)
186      test_observer_->OnPostGetFaviconTask();
187
188    event_.Wait();
189    if (!favicon_bitmap_result_.is_valid())
190      return false;
191
192    scoped_refptr<base::RefCountedMemory> bitmap_data =
193        favicon_bitmap_result_.bitmap_data;
194    image_data->assign(bitmap_data->front(),
195                       bitmap_data->front() + bitmap_data->size());
196    return true;
197  }
198
199  return false;
200}
201
202void SQLiteCursor::GetFaviconForIDInUIThread(
203    chrome::FaviconID id,
204    const FaviconService::FaviconRawCallback& callback) {
205  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
206  if (!tracker_.get())
207    tracker_.reset(new CancelableTaskTracker());
208  favicon_service_->GetLargestRawFaviconForID(id, callback, tracker_.get());
209}
210
211
212void SQLiteCursor::OnFaviconData(
213    const chrome::FaviconBitmapResult& bitmap_result) {
214  favicon_bitmap_result_ = bitmap_result;
215  event_.Signal();
216  if (test_observer_)
217    test_observer_->OnGetFaviconResult();
218}
219
220void SQLiteCursor::OnMoved(AndroidHistoryProviderService::Handle handle,
221                           int pos) {
222  position_ = pos;
223  event_.Signal();
224  if (test_observer_)
225    // Notified test_observer on UI thread instead of the one it will wait.
226    test_observer_->OnGetMoveToResult();
227}
228
229void SQLiteCursor::CancelAllRequests(base::WaitableEvent* finished) {
230  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
231
232  // Destruction will cancel all pending tasks.
233  consumer_.reset();
234  tracker_.reset();
235
236  if (finished)
237    finished->Signal();
238}
239
240SQLiteCursor::JavaColumnType SQLiteCursor::GetColumnTypeInternal(int column) {
241  if (column == statement_->favicon_index())
242    return SQLiteCursor::BLOB;
243
244  return ToJavaColumnType(statement_->statement()->ColumnType(column));
245}
246
247void SQLiteCursor::RunMoveStatementOnUIThread(int pos) {
248  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
249  if (!consumer_.get())
250    consumer_.reset(new CancelableRequestConsumer());
251  service_->MoveStatement(
252      statement_, position_, pos, consumer_.get(),
253      base::Bind(&SQLiteCursor::OnMoved, base::Unretained(this)));
254}
255