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