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 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 tracker_.reset(); 171 service_->CloseStatement(statement_); 172 delete this; 173 } 174 175 bool 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 203 void 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 212 void 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 220 void 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 228 SQLiteCursor::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 235 void 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