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 18 using base::android::ConvertUTF8ToJavaString; 19 using base::android::ScopedJavaLocalRef; 20 using content::BrowserThread; 21 22 namespace { 23 24 SQLiteCursor::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 45 SQLiteCursor::TestObserver::TestObserver() { 46 } 47 48 SQLiteCursor::TestObserver::~TestObserver() { 49 } 50 51 ScopedJavaLocalRef<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 62 bool SQLiteCursor::RegisterSqliteCursor(JNIEnv* env) { 63 return RegisterNativesImpl(env); 64 } 65 66 jint 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 76 ScopedJavaLocalRef<jobjectArray> SQLiteCursor::GetColumnNames(JNIEnv* env, 77 jobject obj) { 78 return base::android::ToJavaArrayOfStrings(env, column_names_); 79 } 80 81 ScopedJavaLocalRef<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 89 jlong SQLiteCursor::GetLong(JNIEnv* env, jobject obj, jint column) { 90 return statement_->statement()->ColumnInt64(column); 91 } 92 93 jint SQLiteCursor::GetInt(JNIEnv* env, jobject obj, jint column) { 94 return statement_->statement()->ColumnInt(column); 95 } 96 97 jdouble SQLiteCursor::GetDouble(JNIEnv* env, jobject obj, jint column) { 98 return statement_->statement()->ColumnDouble(column); 99 } 100 101 ScopedJavaLocalRef<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 116 jboolean SQLiteCursor::IsNull(JNIEnv* env, jobject obj, jint column) { 117 return NULL_TYPE == GetColumnTypeInternal(column) ? JNI_TRUE : JNI_FALSE; 118 } 119 120 jint 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 131 jint SQLiteCursor::GetColumnType(JNIEnv* env, jobject obj, jint column) { 132 return GetColumnTypeInternal(column); 133 } 134 135 void 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 149 SQLiteCursor::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 163 SQLiteCursor::~SQLiteCursor() { 164 } 165 166 void 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 176 bool 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 204 void SQLiteCursor::GetFaviconForIDInUIThread( 205 favicon_base::FaviconID id, 206 const favicon_base::FaviconRawBitmapCallback& 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 213 void SQLiteCursor::OnFaviconData( 214 const favicon_base::FaviconRawBitmapResult& bitmap_result) { 215 favicon_bitmap_result_ = bitmap_result; 216 event_.Signal(); 217 if (test_observer_) 218 test_observer_->OnGetFaviconResult(); 219 } 220 221 void 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 230 SQLiteCursor::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 237 void 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