| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/history/android/sqlite_cursor.h" |
| |
| #include "base/android/jni_android.h" |
| #include "base/android/jni_array.h" |
| #include "base/android/jni_string.h" |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "chrome/browser/history/android/android_history_types.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "jni/SQLiteCursor_jni.h" |
| #include "sql/statement.h" |
| |
| using base::android::ConvertUTF8ToJavaString; |
| using base::android::ScopedJavaLocalRef; |
| using content::BrowserThread; |
| |
| namespace { |
| |
| SQLiteCursor::JavaColumnType ToJavaColumnType(sql::ColType type) { |
| switch (type) { |
| case sql::COLUMN_TYPE_INTEGER: |
| return SQLiteCursor::NUMERIC; |
| case sql::COLUMN_TYPE_FLOAT: |
| return SQLiteCursor::DOUBLE; |
| case sql::COLUMN_TYPE_TEXT: |
| return SQLiteCursor::LONG_VAR_CHAR; |
| case sql::COLUMN_TYPE_BLOB: |
| return SQLiteCursor::BLOB; |
| case sql::COLUMN_TYPE_NULL: |
| return SQLiteCursor::NULL_TYPE; |
| default: |
| NOTREACHED(); |
| } |
| return SQLiteCursor::NULL_TYPE; |
| } |
| |
| } // namespace. |
| |
| |
| SQLiteCursor::TestObserver::TestObserver() { |
| } |
| |
| SQLiteCursor::TestObserver::~TestObserver() { |
| } |
| |
| ScopedJavaLocalRef<jobject> SQLiteCursor::NewJavaSqliteCursor( |
| JNIEnv* env, |
| const std::vector<std::string>& column_names, |
| history::AndroidStatement* statement, |
| AndroidHistoryProviderService* service, |
| FaviconService* favicon_service) { |
| SQLiteCursor* cursor = new SQLiteCursor(column_names, statement, service, |
| favicon_service); |
| return Java_SQLiteCursor_create(env, reinterpret_cast<intptr_t>(cursor)); |
| } |
| |
| bool SQLiteCursor::RegisterSqliteCursor(JNIEnv* env) { |
| return RegisterNativesImpl(env); |
| } |
| |
| jint SQLiteCursor::GetCount(JNIEnv* env, jobject obj) { |
| // Moves to maxium possible position so we will reach the last row, then finds |
| // out the total number of rows. |
| int current_position = position_; |
| int count = MoveTo(env, obj, std::numeric_limits<int>::max() - 1) + 1; |
| // Moves back to the previous position. |
| MoveTo(env, obj, current_position); |
| return count; |
| } |
| |
| ScopedJavaLocalRef<jobjectArray> SQLiteCursor::GetColumnNames(JNIEnv* env, |
| jobject obj) { |
| return base::android::ToJavaArrayOfStrings(env, column_names_); |
| } |
| |
| ScopedJavaLocalRef<jstring> SQLiteCursor::GetString(JNIEnv* env, |
| jobject obj, |
| jint column) { |
| base::string16 value = statement_->statement()->ColumnString16(column); |
| return ScopedJavaLocalRef<jstring>(env, |
| env->NewString(value.data(), value.size())); |
| } |
| |
| jlong SQLiteCursor::GetLong(JNIEnv* env, jobject obj, jint column) { |
| return statement_->statement()->ColumnInt64(column); |
| } |
| |
| jint SQLiteCursor::GetInt(JNIEnv* env, jobject obj, jint column) { |
| return statement_->statement()->ColumnInt(column); |
| } |
| |
| jdouble SQLiteCursor::GetDouble(JNIEnv* env, jobject obj, jint column) { |
| return statement_->statement()->ColumnDouble(column); |
| } |
| |
| ScopedJavaLocalRef<jbyteArray> SQLiteCursor::GetBlob(JNIEnv* env, |
| jobject obj, |
| jint column) { |
| std::vector<unsigned char> blob; |
| |
| // Assume the client will only get favicon using GetBlob. |
| if (statement_->favicon_index() == column) { |
| if (!GetFavicon(statement_->statement()->ColumnInt(column), &blob)) |
| return ScopedJavaLocalRef<jbyteArray>(); |
| } else { |
| statement_->statement()->ColumnBlobAsVector(column, &blob); |
| } |
| return base::android::ToJavaByteArray(env, &blob[0], blob.size()); |
| } |
| |
| jboolean SQLiteCursor::IsNull(JNIEnv* env, jobject obj, jint column) { |
| return NULL_TYPE == GetColumnTypeInternal(column) ? JNI_TRUE : JNI_FALSE; |
| } |
| |
| jint SQLiteCursor::MoveTo(JNIEnv* env, jobject obj, jint pos) { |
| BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| base::Bind(&SQLiteCursor::RunMoveStatementOnUIThread, |
| base::Unretained(this), pos)); |
| if (test_observer_) |
| test_observer_->OnPostMoveToTask(); |
| |
| event_.Wait(); |
| return position_; |
| } |
| |
| jint SQLiteCursor::GetColumnType(JNIEnv* env, jobject obj, jint column) { |
| return GetColumnTypeInternal(column); |
| } |
| |
| void SQLiteCursor::Destroy(JNIEnv* env, jobject obj) { |
| // We do our best to cleanup when Destroy() is called from Java's finalize() |
| // where the UI message loop might stop running or in the process of shutting |
| // down, as the whole process will be destroyed soon, it's fine to leave some |
| // objects out there. |
| if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| DestroyOnUIThread(); |
| } else if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| base::Bind(&SQLiteCursor::DestroyOnUIThread, |
| base::Unretained(this)))) { |
| delete this; |
| } |
| } |
| |
| SQLiteCursor::SQLiteCursor(const std::vector<std::string>& column_names, |
| history::AndroidStatement* statement, |
| AndroidHistoryProviderService* service, |
| FaviconService* favicon_service) |
| : position_(-1), |
| event_(false, false), |
| statement_(statement), |
| column_names_(column_names), |
| service_(service), |
| favicon_service_(favicon_service), |
| count_(-1), |
| test_observer_(NULL) { |
| } |
| |
| SQLiteCursor::~SQLiteCursor() { |
| } |
| |
| void SQLiteCursor::DestroyOnUIThread() { |
| // Consumer requests were set in the UI thread. They must be cancelled |
| // using the same thread. |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| consumer_.reset(); |
| tracker_.reset(); |
| service_->CloseStatement(statement_); |
| delete this; |
| } |
| |
| bool SQLiteCursor::GetFavicon(chrome::FaviconID id, |
| std::vector<unsigned char>* image_data) { |
| if (id) { |
| BrowserThread::PostTask( |
| BrowserThread::UI, |
| FROM_HERE, |
| base::Bind(&SQLiteCursor::GetFaviconForIDInUIThread, |
| base::Unretained(this), id, |
| base::Bind(&SQLiteCursor::OnFaviconData, |
| base::Unretained(this)))); |
| |
| if (test_observer_) |
| test_observer_->OnPostGetFaviconTask(); |
| |
| event_.Wait(); |
| if (!favicon_bitmap_result_.is_valid()) |
| return false; |
| |
| scoped_refptr<base::RefCountedMemory> bitmap_data = |
| favicon_bitmap_result_.bitmap_data; |
| image_data->assign(bitmap_data->front(), |
| bitmap_data->front() + bitmap_data->size()); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void SQLiteCursor::GetFaviconForIDInUIThread( |
| chrome::FaviconID id, |
| const FaviconService::FaviconRawCallback& callback) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| if (!tracker_.get()) |
| tracker_.reset(new CancelableTaskTracker()); |
| favicon_service_->GetLargestRawFaviconForID(id, callback, tracker_.get()); |
| } |
| |
| |
| void SQLiteCursor::OnFaviconData( |
| const chrome::FaviconBitmapResult& bitmap_result) { |
| favicon_bitmap_result_ = bitmap_result; |
| event_.Signal(); |
| if (test_observer_) |
| test_observer_->OnGetFaviconResult(); |
| } |
| |
| void SQLiteCursor::OnMoved(AndroidHistoryProviderService::Handle handle, |
| int pos) { |
| position_ = pos; |
| event_.Signal(); |
| if (test_observer_) |
| // Notified test_observer on UI thread instead of the one it will wait. |
| test_observer_->OnGetMoveToResult(); |
| } |
| |
| SQLiteCursor::JavaColumnType SQLiteCursor::GetColumnTypeInternal(int column) { |
| if (column == statement_->favicon_index()) |
| return SQLiteCursor::BLOB; |
| |
| return ToJavaColumnType(statement_->statement()->ColumnType(column)); |
| } |
| |
| void SQLiteCursor::RunMoveStatementOnUIThread(int pos) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| if (!consumer_.get()) |
| consumer_.reset(new CancelableRequestConsumer()); |
| service_->MoveStatement( |
| statement_, position_, pos, consumer_.get(), |
| base::Bind(&SQLiteCursor::OnMoved, base::Unretained(this))); |
| } |