Home | History | Annotate | Download | only in android
      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/android_provider_backend.h"
      6 
      7 #include "base/i18n/case_conversion.h"
      8 #include "chrome/browser/chrome_notification_types.h"
      9 #include "chrome/browser/history/android/android_time.h"
     10 #include "chrome/browser/history/android/android_urls_sql_handler.h"
     11 #include "chrome/browser/history/android/bookmark_model_sql_handler.h"
     12 #include "chrome/browser/history/android/favicon_sql_handler.h"
     13 #include "chrome/browser/history/android/urls_sql_handler.h"
     14 #include "chrome/browser/history/android/visit_sql_handler.h"
     15 #include "chrome/browser/history/history_backend.h"
     16 #include "chrome/browser/history/history_database.h"
     17 #include "chrome/browser/history/thumbnail_database.h"
     18 #include "components/history/core/browser/history_client.h"
     19 #include "components/history/core/browser/keyword_search_term.h"
     20 #include "sql/connection.h"
     21 
     22 
     23 namespace history {
     24 
     25 
     26 // Helpers --------------------------------------------------------------------
     27 
     28 namespace {
     29 
     30 const char kVirtualHistoryAndBookmarkTable[] =
     31     "SELECT android_urls.id AS _id, "
     32         "android_cache_db.bookmark_cache.created_time AS created, "
     33         "urls.title AS title, android_urls.raw_url AS url, "
     34         "urls.visit_count AS visits, "
     35         "android_cache_db.bookmark_cache.last_visit_time AS date, "
     36         "android_cache_db.bookmark_cache.bookmark AS bookmark, "
     37         "android_cache_db.bookmark_cache.favicon_id AS favicon, "
     38         "urls.id AS url_id, urls.url AS urls_url, "
     39     // TODO (michaelbai) : Remove folder column once we remove it from Android
     40     // framework.
     41     // Android framework assumes 'folder' column exist in the table, the row is
     42     // the bookmark once folder is 0, though it is not part of public API, it
     43     // has to be added and set as 0 when the row is bookmark.
     44         "(CASE WHEN android_cache_db.bookmark_cache.bookmark IS 0 "
     45         "THEN 1 ELSE 0 END) as folder "
     46     "FROM (android_urls JOIN urls on (android_urls.url_id = urls.id) "
     47         "LEFT JOIN android_cache_db.bookmark_cache "
     48         "on (android_urls.url_id = android_cache_db.bookmark_cache.url_id))";
     49 
     50 const char kURLUpdateClause[] =
     51     "SELECT urls.id, urls.last_visit_time, created_time, urls.url "
     52     "FROM urls LEFT JOIN "
     53         "(SELECT url as visit_url, min(visit_time) as created_time"
     54         " FROM visits GROUP BY url) ON (visit_url = urls.id) ";
     55 
     56 const char kSearchTermUpdateClause[] =
     57     "SELECT keyword_search_terms.term, max(urls.last_visit_time) "
     58     "FROM keyword_search_terms JOIN urls ON "
     59         "(keyword_search_terms.url_id = urls.id) "
     60     "GROUP BY keyword_search_terms.term";
     61 
     62 void BindStatement(const std::vector<base::string16>& selection_args,
     63                    sql::Statement* statement,
     64                    int* col_index) {
     65   for (std::vector<base::string16>::const_iterator i = selection_args.begin();
     66        i != selection_args.end(); ++i) {
     67     // Using the same method as Android, binding all argument as String.
     68     statement->BindString16(*col_index, *i);
     69     ++(*col_index);
     70   }
     71 }
     72 
     73 bool IsHistoryAndBookmarkRowValid(const HistoryAndBookmarkRow& row) {
     74   // The caller should make sure both/neither Raw URL and/nor URL should be set.
     75   DCHECK(row.is_value_set_explicitly(HistoryAndBookmarkRow::RAW_URL) ==
     76          row.is_value_set_explicitly(HistoryAndBookmarkRow::URL));
     77 
     78   // The following cases are checked:
     79   // a. Last visit time or created time is large than now.
     80   // b. Last visit time is less than created time.
     81   // c. Created time and last visit time is different, but visit count is less
     82   //    than 2.
     83   // d. The difference between created and last visit time is less than
     84   //    visit_count.
     85   // e. Visit count is 0 or 1 and both last visit time and created time are set
     86   //    explicitly, but the time is different or created time is not UnixEpoch.
     87   if (row.is_value_set_explicitly(HistoryAndBookmarkRow::LAST_VISIT_TIME) &&
     88       row.last_visit_time() > base::Time::Now())
     89     return false;
     90 
     91   if (row.is_value_set_explicitly(HistoryAndBookmarkRow::CREATED) &&
     92       row.created() > base::Time::Now())
     93     return false;
     94 
     95   if (row.is_value_set_explicitly(HistoryAndBookmarkRow::LAST_VISIT_TIME) &&
     96       row.is_value_set_explicitly(HistoryAndBookmarkRow::CREATED)) {
     97     if (row.created() > row.last_visit_time())
     98       return false;
     99 
    100     if (row.is_value_set_explicitly(HistoryAndBookmarkRow::VISIT_COUNT) &&
    101         row.is_value_set_explicitly(HistoryAndBookmarkRow::CREATED) &&
    102         row.is_value_set_explicitly(HistoryAndBookmarkRow::LAST_VISIT_TIME)) {
    103       if (row.created() != row.last_visit_time() &&
    104           row.created() != base::Time::UnixEpoch() &&
    105           (row.visit_count() == 0 || row.visit_count() == 1))
    106         return false;
    107 
    108       if (row.last_visit_time().ToInternalValue() -
    109           row.created().ToInternalValue() < row.visit_count())
    110         return false;
    111     }
    112   }
    113   return true;
    114 }
    115 
    116 void RunNotifyFaviconChanged(HistoryBackend::Delegate* delegate,
    117                              scoped_ptr<std::set<GURL> > changed_favicons) {
    118   delegate->NotifyFaviconChanged(*changed_favicons);
    119 }
    120 
    121 }  // namespace
    122 
    123 // AndroidProviderBackend::ScopedTransaction ----------------------------------
    124 
    125 AndroidProviderBackend::ScopedTransaction::ScopedTransaction(
    126     HistoryDatabase* history_db,
    127     ThumbnailDatabase* thumbnail_db)
    128     : history_db_(history_db),
    129       thumbnail_db_(thumbnail_db),
    130       committed_(false),
    131       history_transaction_nesting_(history_db_->transaction_nesting()),
    132       thumbnail_transaction_nesting_(
    133           thumbnail_db_ ? thumbnail_db_->transaction_nesting() : 0) {
    134   // Commit all existing transactions since the AndroidProviderBackend's
    135   // transaction is very like to be rolled back when compared with the others.
    136   // The existing transactions have been scheduled to commit by
    137   // ScheduleCommit in HistoryBackend and the same number of transaction
    138   // will be created after this scoped transaction ends, there should have no
    139   // issue to directly commit all transactions here.
    140   int count = history_transaction_nesting_;
    141   while (count--)
    142     history_db_->CommitTransaction();
    143   history_db_->BeginTransaction();
    144 
    145   if (thumbnail_db_) {
    146     count = thumbnail_transaction_nesting_;
    147     while (count--)
    148       thumbnail_db_->CommitTransaction();
    149     thumbnail_db_->BeginTransaction();
    150   }
    151 }
    152 
    153 AndroidProviderBackend::ScopedTransaction::~ScopedTransaction() {
    154   if (!committed_) {
    155     history_db_->RollbackTransaction();
    156     if (thumbnail_db_)
    157       thumbnail_db_->RollbackTransaction();
    158   }
    159   // There is no transaction now.
    160   DCHECK_EQ(0, history_db_->transaction_nesting());
    161   DCHECK(!thumbnail_db_ || 0 == thumbnail_db_->transaction_nesting());
    162 
    163   int count = history_transaction_nesting_;
    164   while (count--)
    165     history_db_->BeginTransaction();
    166 
    167   if (thumbnail_db_) {
    168     count = thumbnail_transaction_nesting_;
    169     while (count--)
    170       thumbnail_db_->BeginTransaction();
    171   }
    172 }
    173 
    174 void AndroidProviderBackend::ScopedTransaction::Commit() {
    175   DCHECK(!committed_);
    176   history_db_->CommitTransaction();
    177   if (thumbnail_db_)
    178     thumbnail_db_->CommitTransaction();
    179   committed_ = true;
    180 }
    181 
    182 
    183 // AndroidProviderBackend -----------------------------------------------------
    184 
    185 AndroidProviderBackend::AndroidProviderBackend(
    186     const base::FilePath& db_name,
    187     HistoryDatabase* history_db,
    188     ThumbnailDatabase* thumbnail_db,
    189     HistoryClient* history_client,
    190     HistoryBackend::Delegate* delegate)
    191     : android_cache_db_filename_(db_name),
    192       db_(&history_db->GetDB()),
    193       history_db_(history_db),
    194       thumbnail_db_(thumbnail_db),
    195       history_client_(history_client),
    196       initialized_(false),
    197       delegate_(delegate) {
    198   DCHECK(delegate_);
    199 }
    200 
    201 AndroidProviderBackend::~AndroidProviderBackend() {
    202 }
    203 
    204 AndroidStatement* AndroidProviderBackend::QueryHistoryAndBookmarks(
    205     const std::vector<HistoryAndBookmarkRow::ColumnID>& projections,
    206     const std::string& selection,
    207     const std::vector<base::string16>& selection_args,
    208     const std::string& sort_order) {
    209   if (projections.empty())
    210     return NULL;
    211 
    212   ScopedTransaction transaction(history_db_, thumbnail_db_);
    213 
    214   if (!EnsureInitializedAndUpdated())
    215     return NULL;
    216 
    217   transaction.Commit();
    218 
    219   return QueryHistoryAndBookmarksInternal(projections, selection,
    220                                           selection_args, sort_order);
    221 }
    222 
    223 bool AndroidProviderBackend::UpdateHistoryAndBookmarks(
    224     const HistoryAndBookmarkRow& row,
    225     const std::string& selection,
    226     const std::vector<base::string16>& selection_args,
    227     int* updated_count) {
    228   HistoryNotifications notifications;
    229 
    230   ScopedTransaction transaction(history_db_, thumbnail_db_);
    231 
    232   if (!UpdateHistoryAndBookmarks(row, selection, selection_args, updated_count,
    233                                  &notifications))
    234     return false;
    235 
    236   transaction.Commit();
    237   BroadcastNotifications(&notifications);
    238   return true;
    239 }
    240 
    241 AndroidURLID AndroidProviderBackend::InsertHistoryAndBookmark(
    242     const HistoryAndBookmarkRow& values) {
    243   HistoryNotifications notifications;
    244 
    245   ScopedTransaction transaction(history_db_, thumbnail_db_);
    246 
    247   AndroidURLID id = InsertHistoryAndBookmark(values, true, &notifications);
    248   if (!id)
    249     return 0;
    250 
    251   transaction.Commit();
    252   BroadcastNotifications(&notifications);
    253   return id;
    254 }
    255 
    256 bool AndroidProviderBackend::DeleteHistoryAndBookmarks(
    257     const std::string& selection,
    258     const std::vector<base::string16>& selection_args,
    259     int* deleted_count) {
    260   HistoryNotifications notifications;
    261 
    262   ScopedTransaction transaction(history_db_, thumbnail_db_);
    263 
    264   if (!DeleteHistoryAndBookmarks(selection, selection_args, deleted_count,
    265                                  &notifications))
    266     return false;
    267 
    268   transaction.Commit();
    269   BroadcastNotifications(&notifications);
    270   return true;
    271 }
    272 
    273 bool AndroidProviderBackend::DeleteHistory(
    274     const std::string& selection,
    275     const std::vector<base::string16>& selection_args,
    276     int* deleted_count) {
    277   HistoryNotifications notifications;
    278 
    279   ScopedTransaction transaction(history_db_, thumbnail_db_);
    280 
    281   if (!DeleteHistory(selection, selection_args, deleted_count, &notifications))
    282     return false;
    283 
    284   transaction.Commit();
    285   BroadcastNotifications(&notifications);
    286   return true;
    287 }
    288 
    289 bool AndroidProviderBackend::UpdateHistoryAndBookmarks(
    290     const HistoryAndBookmarkRow& row,
    291     const std::string& selection,
    292     const std::vector<base::string16>& selection_args,
    293     int* updated_count,
    294     HistoryNotifications* notifications) {
    295   if (!IsHistoryAndBookmarkRowValid(row))
    296     return false;
    297 
    298   if (row.is_value_set_explicitly(HistoryAndBookmarkRow::ID))
    299     return false;
    300 
    301   if (!EnsureInitializedAndUpdated())
    302     return false;
    303 
    304   TableIDRows ids_set;
    305   if (!GetSelectedURLs(selection, selection_args, &ids_set))
    306     return false;
    307 
    308   if (ids_set.empty()) {
    309     *updated_count = 0;
    310     return true;
    311   }
    312 
    313   // URL can not be updated, we simulate the update.
    314   if (row.is_value_set_explicitly(HistoryAndBookmarkRow::URL)) {
    315     // Only one row's URL can be updated at a time as we can't have multiple
    316     // rows have the same URL.
    317     if (ids_set.size() != 1)
    318       return false;
    319 
    320     HistoryAndBookmarkRow new_row = row;
    321     if (!SimulateUpdateURL(new_row, ids_set, notifications))
    322       return false;
    323     *updated_count = 1;
    324     return true;
    325   }
    326 
    327   for (std::vector<SQLHandler*>::iterator i =
    328        sql_handlers_.begin(); i != sql_handlers_.end(); ++i) {
    329     if ((*i)->HasColumnIn(row)) {
    330       if (!(*i)->Update(row, ids_set))
    331         return false;
    332     }
    333   }
    334   *updated_count = ids_set.size();
    335 
    336   scoped_ptr<URLsModifiedDetails> modified(new URLsModifiedDetails);
    337   scoped_ptr<std::set<GURL> > favicon(new std::set<GURL>);
    338 
    339   for (TableIDRows::const_iterator i = ids_set.begin(); i != ids_set.end();
    340        ++i) {
    341     if (row.is_value_set_explicitly(HistoryAndBookmarkRow::TITLE) ||
    342         row.is_value_set_explicitly(HistoryAndBookmarkRow::VISIT_COUNT) ||
    343         row.is_value_set_explicitly(HistoryAndBookmarkRow::LAST_VISIT_TIME)) {
    344       URLRow url_row;
    345       if (!history_db_->GetURLRow(i->url_id, &url_row))
    346         return false;
    347       modified->changed_urls.push_back(url_row);
    348     }
    349     if (thumbnail_db_ &&
    350         row.is_value_set_explicitly(HistoryAndBookmarkRow::FAVICON))
    351       favicon->insert(i->url);
    352   }
    353 
    354   if (!modified->changed_urls.empty()) {
    355     scoped_ptr<HistoryDetails> details = modified.PassAs<HistoryDetails>();
    356     notifications->push_back(
    357         base::Bind(&HistoryBackend::Delegate::BroadcastNotifications,
    358                    base::Unretained(delegate_),
    359                    chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
    360                    base::Passed(&details)));
    361   }
    362 
    363   if (!favicon->empty()) {
    364     notifications->push_back(base::Bind(&RunNotifyFaviconChanged,
    365                                         base::Unretained(delegate_),
    366                                         base::Passed(&favicon)));
    367   }
    368 
    369   return true;
    370 }
    371 
    372 AndroidURLID AndroidProviderBackend::InsertHistoryAndBookmark(
    373     const HistoryAndBookmarkRow& values,
    374     bool ensure_initialized_and_updated,
    375     HistoryNotifications* notifications) {
    376   if (!IsHistoryAndBookmarkRowValid(values))
    377     return false;
    378 
    379   if (ensure_initialized_and_updated && !EnsureInitializedAndUpdated())
    380     return 0;
    381 
    382   DCHECK(values.is_value_set_explicitly(HistoryAndBookmarkRow::URL));
    383   // Make a copy of values as we need change it during insert.
    384   HistoryAndBookmarkRow row = values;
    385   for (std::vector<SQLHandler*>::iterator i =
    386        sql_handlers_.begin(); i != sql_handlers_.end(); ++i) {
    387     if (!(*i)->Insert(&row))
    388       return 0;
    389   }
    390 
    391   URLRow url_row;
    392   if (!history_db_->GetURLRow(row.url_id(), &url_row))
    393     return false;
    394 
    395   scoped_ptr<URLsModifiedDetails> modified(new URLsModifiedDetails);
    396   modified->changed_urls.push_back(url_row);
    397 
    398   scoped_ptr<std::set<GURL> > favicon;
    399   // No favicon should be changed if the thumbnail_db_ is not available.
    400   if (row.is_value_set_explicitly(HistoryAndBookmarkRow::FAVICON) &&
    401       row.favicon_valid() && thumbnail_db_) {
    402     favicon.reset(new std::set<GURL>);
    403     favicon->insert(url_row.url());
    404   }
    405 
    406   scoped_ptr<HistoryDetails> details = modified.PassAs<HistoryDetails>();
    407   notifications->push_back(
    408       base::Bind(&HistoryBackend::Delegate::BroadcastNotifications,
    409                  base::Unretained(delegate_),
    410                  chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
    411                  base::Passed(&details)));
    412 
    413   if (favicon) {
    414     DCHECK(!favicon->empty());
    415     notifications->push_back(base::Bind(&RunNotifyFaviconChanged,
    416                                         base::Unretained(delegate_),
    417                                         base::Passed(&favicon)));
    418   }
    419 
    420   return row.id();
    421 }
    422 
    423 bool AndroidProviderBackend::DeleteHistoryAndBookmarks(
    424     const std::string& selection,
    425     const std::vector<base::string16>& selection_args,
    426     int * deleted_count,
    427     HistoryNotifications* notifications) {
    428   if (!EnsureInitializedAndUpdated())
    429     return false;
    430 
    431   TableIDRows ids_set;
    432   if (!GetSelectedURLs(selection, selection_args, &ids_set))
    433     return false;
    434 
    435   if (ids_set.empty()) {
    436     *deleted_count = 0;
    437     return true;
    438   }
    439 
    440   if (!DeleteHistoryInternal(ids_set, true, notifications))
    441     return false;
    442 
    443   *deleted_count = ids_set.size();
    444 
    445   return true;
    446 }
    447 
    448 bool AndroidProviderBackend::DeleteHistory(
    449     const std::string& selection,
    450     const std::vector<base::string16>& selection_args,
    451     int* deleted_count,
    452     HistoryNotifications* notifications) {
    453   if (!EnsureInitializedAndUpdated())
    454     return false;
    455 
    456   TableIDRows ids_set;
    457   if (!GetSelectedURLs(selection, selection_args, &ids_set))
    458     return false;
    459 
    460   if (ids_set.empty()) {
    461     *deleted_count = 0;
    462     return true;
    463   }
    464 
    465   *deleted_count = ids_set.size();
    466 
    467   // Get the bookmarked rows.
    468   std::vector<HistoryAndBookmarkRow> bookmarks;
    469   for (TableIDRows::const_iterator i = ids_set.begin(); i != ids_set.end();
    470        ++i) {
    471     if (i->bookmarked) {
    472       AndroidURLRow android_url_row;
    473       if (!history_db_->GetAndroidURLRow(i->url_id, &android_url_row))
    474         return false;
    475       HistoryAndBookmarkRow row;
    476       row.set_raw_url(android_url_row.raw_url);
    477       row.set_url(i->url);
    478       // Set the visit time to the UnixEpoch since that's when the Android
    479       // system time starts. The Android have a CTS testcase for this.
    480       row.set_last_visit_time(base::Time::UnixEpoch());
    481       row.set_visit_count(0);
    482       // We don't want to change the bookmark model, so set_is_bookmark() is
    483       // not called.
    484       bookmarks.push_back(row);
    485     }
    486   }
    487 
    488   // Don't delete the bookmark from bookmark model when deleting the history.
    489   if (!DeleteHistoryInternal(ids_set, false, notifications))
    490     return false;
    491 
    492   for (std::vector<HistoryAndBookmarkRow>::const_iterator i = bookmarks.begin();
    493        i != bookmarks.end(); ++i) {
    494     // Don't update the tables, otherwise, the bookmarks will be added to
    495     // database during UpdateBookmark(), then the insertion will fail.
    496     // We can't rely on UpdateBookmark() to insert the bookmarks into history
    497     // database as the raw_url will be lost.
    498     if (!InsertHistoryAndBookmark(*i, false, notifications))
    499       return false;
    500   }
    501   return true;
    502 }
    503 
    504 AndroidStatement* AndroidProviderBackend::QuerySearchTerms(
    505     const std::vector<SearchRow::ColumnID>& projections,
    506     const std::string& selection,
    507     const std::vector<base::string16>& selection_args,
    508     const std::string& sort_order) {
    509   if (projections.empty())
    510     return NULL;
    511 
    512   if (!EnsureInitializedAndUpdated())
    513     return NULL;
    514 
    515   std::string sql;
    516   sql.append("SELECT ");
    517   AppendSearchResultColumn(projections, &sql);
    518   sql.append(" FROM android_cache_db.search_terms ");
    519 
    520   if (!selection.empty()) {
    521     sql.append(" WHERE ");
    522     sql.append(selection);
    523   }
    524 
    525   if (!sort_order.empty()) {
    526     sql.append(" ORDER BY ");
    527     sql.append(sort_order);
    528   }
    529 
    530   scoped_ptr<sql::Statement> statement(new sql::Statement(
    531       db_->GetUniqueStatement(sql.c_str())));
    532   int count = 0;
    533   BindStatement(selection_args, statement.get(), &count);
    534   if (!statement->is_valid()) {
    535     LOG(ERROR) << db_->GetErrorMessage();
    536     return NULL;
    537   }
    538   sql::Statement* result = statement.release();
    539   return new AndroidStatement(result, -1);
    540 }
    541 
    542 bool AndroidProviderBackend::UpdateSearchTerms(
    543     const SearchRow& row,
    544     const std::string& selection,
    545     const std::vector<base::string16>& selection_args,
    546     int* update_count) {
    547   if (!EnsureInitializedAndUpdated())
    548     return false;
    549 
    550   SearchTerms search_terms;
    551   if (!GetSelectedSearchTerms(selection, selection_args, &search_terms))
    552     return false;
    553 
    554   // We can not update search term if multiple row selected.
    555   if (row.is_value_set_explicitly(SearchRow::SEARCH_TERM) &&
    556       search_terms.size() > 1)
    557     return false;
    558 
    559   *update_count = search_terms.size();
    560 
    561   if (search_terms.empty())
    562     return true;
    563 
    564   if (row.is_value_set_explicitly(SearchRow::SEARCH_TERM)) {
    565     SearchTermRow search_term_row;
    566     SearchRow search_row = row;
    567     if (!history_db_->GetSearchTerm(search_terms[0], &search_term_row))
    568       return false;
    569 
    570     search_term_row.term = search_row.search_term();
    571     if (!search_row.is_value_set_explicitly(SearchRow::SEARCH_TIME))
    572       search_row.set_search_time(search_term_row.last_visit_time);
    573     else
    574       search_term_row.last_visit_time = search_row.search_time();
    575 
    576     // Delete the original search term.
    577     if (!history_db_->DeleteKeywordSearchTerm(search_terms[0]))
    578       return false;
    579 
    580     // Add the new one.
    581     if (!AddSearchTerm(search_row))
    582       return false;
    583 
    584     // Update the cache table so the id will not be changed.
    585     if (!history_db_->UpdateSearchTerm(search_term_row.id, search_term_row))
    586       return false;
    587 
    588      return true;
    589   }
    590 
    591   for (SearchTerms::const_iterator i = search_terms.begin();
    592        i != search_terms.end(); ++i) {
    593     SearchTermRow search_term_row;
    594     if (!history_db_->GetSearchTerm(*i, &search_term_row))
    595       return false;
    596 
    597     // Check whether the given search time less than the existing one.
    598     if (search_term_row.last_visit_time > row.search_time())
    599       return false;
    600 
    601     std::vector<KeywordSearchTermRow> search_term_rows;
    602     if (!history_db_->GetKeywordSearchTermRows(*i, &search_term_rows) ||
    603         search_term_rows.empty())
    604       return false;
    605 
    606     // Actually only search_time update. As there might multiple URLs
    607     // asocciated with the keyword, Just update the first one's last_visit_time.
    608     URLRow url_row;
    609     if (!history_db_->GetURLRow(search_term_rows[0].url_id, &url_row))
    610       return false;
    611 
    612     HistoryAndBookmarkRow bookmark_row;
    613     bookmark_row.set_last_visit_time(row.search_time());
    614     TableIDRow table_id_row;
    615     table_id_row.url_id = url_row.id();
    616     TableIDRows table_id_rows;
    617     table_id_rows.push_back(table_id_row);
    618     if (!urls_handler_->Update(bookmark_row, table_id_rows))
    619       return false;
    620 
    621     if (!visit_handler_->Update(bookmark_row, table_id_rows))
    622       return false;
    623   }
    624   return true;
    625 }
    626 
    627 SearchTermID AndroidProviderBackend::InsertSearchTerm(
    628     const SearchRow& values) {
    629   if (!EnsureInitializedAndUpdated())
    630     return 0;
    631 
    632   if (!AddSearchTerm(values))
    633     return 0;
    634 
    635   SearchTermID id = history_db_->GetSearchTerm(values.search_term(), NULL);
    636   if (!id)
    637     // Note the passed in Time() will be changed in UpdateSearchTermTable().
    638     id = history_db_->AddSearchTerm(values.search_term(), base::Time());
    639   return id;
    640 }
    641 
    642 bool AndroidProviderBackend::DeleteSearchTerms(
    643     const std::string& selection,
    644     const std::vector<base::string16>& selection_args,
    645     int * deleted_count) {
    646   if (!EnsureInitializedAndUpdated())
    647     return false;
    648 
    649   SearchTerms rows;
    650   if (!GetSelectedSearchTerms(selection, selection_args, &rows))
    651     return false;
    652 
    653   *deleted_count = rows.size();
    654   if (rows.empty())
    655     return true;
    656 
    657   for (SearchTerms::const_iterator i = rows.begin(); i != rows.end(); ++i)
    658     if (!history_db_->DeleteKeywordSearchTerm(*i))
    659       return false;
    660   // We don't delete the rows in search_terms table, as once the
    661   // search_terms table is updated with keyword_search_terms, all
    662   // keyword cache not found in the keyword_search_terms will be removed.
    663   return true;
    664 }
    665 
    666 bool AndroidProviderBackend::EnsureInitializedAndUpdated() {
    667   if (!initialized_) {
    668     if (!Init())
    669       return false;
    670   }
    671   return UpdateTables();
    672 }
    673 
    674 bool AndroidProviderBackend::Init() {
    675   urls_handler_.reset(new UrlsSQLHandler(history_db_));
    676   visit_handler_.reset(new VisitSQLHandler(history_db_));
    677   android_urls_handler_.reset(new AndroidURLsSQLHandler(history_db_));
    678   if (thumbnail_db_)
    679     favicon_handler_.reset(new FaviconSQLHandler(thumbnail_db_));
    680   bookmark_model_handler_.reset(new BookmarkModelSQLHandler(history_db_));
    681   // The urls_handler must be pushed first, because the subsequent handlers
    682   // depend on its output.
    683   sql_handlers_.push_back(urls_handler_.get());
    684   sql_handlers_.push_back(visit_handler_.get());
    685   sql_handlers_.push_back(android_urls_handler_.get());
    686   if (favicon_handler_.get())
    687     sql_handlers_.push_back(favicon_handler_.get());
    688   sql_handlers_.push_back(bookmark_model_handler_.get());
    689 
    690   if (!history_db_->CreateAndroidURLsTable())
    691     return false;
    692   if (sql::INIT_OK != history_db_->InitAndroidCacheDatabase(
    693           android_cache_db_filename_))
    694     return false;
    695   initialized_ = true;
    696   return true;
    697 }
    698 
    699 bool AndroidProviderBackend::UpdateTables() {
    700   if (!UpdateVisitedURLs()) {
    701     LOG(ERROR) << "Update of the visisted URLS failed";
    702     return false;
    703   }
    704 
    705   if (!UpdateRemovedURLs()) {
    706     LOG(ERROR) << "Update of the removed URLS failed";
    707     return false;
    708   }
    709 
    710   if (!UpdateBookmarks()) {
    711     LOG(ERROR) << "Update of the bookmarks failed";
    712     return false;
    713   }
    714 
    715   if (!UpdateFavicon()) {
    716     LOG(ERROR) << "Update of the icons failed";
    717     return false;
    718   }
    719 
    720   if (!UpdateSearchTermTable()) {
    721     LOG(ERROR) << "Update of the search_terms failed";
    722     return false;
    723   }
    724   return true;
    725 }
    726 
    727 bool AndroidProviderBackend::UpdateVisitedURLs() {
    728   std::string sql(kURLUpdateClause);
    729   sql.append("WHERE urls.id NOT IN (SELECT url_id FROM android_urls)");
    730   sql::Statement urls_statement(db_->GetCachedStatement(SQL_FROM_HERE,
    731                                                         sql.c_str()));
    732   if (!urls_statement.is_valid()) {
    733     LOG(ERROR) << db_->GetErrorMessage();
    734     return false;
    735   }
    736 
    737   while (urls_statement.Step()) {
    738     if (history_db_->GetAndroidURLRow(urls_statement.ColumnInt64(0), NULL))
    739       continue;
    740     if (!history_db_->AddAndroidURLRow(urls_statement.ColumnString(3),
    741                                        urls_statement.ColumnInt64(0)))
    742       return false;
    743   }
    744 
    745   if (!history_db_->ClearAllBookmarkCache())
    746     return false;
    747 
    748   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE,
    749                                                    kURLUpdateClause));
    750   while (statement.Step()) {
    751     // The last_visit_time and the created time should be same when the visit
    752     // count is 0, this behavior is also required by the Android CTS.
    753     // The created_time could be set to the last_visit_time only when the type
    754     // of the 'created' column is NULL because the left join is used in query
    755     // and there is no row in the visit table when the visit count is 0.
    756     base::Time last_visit_time =
    757         base::Time::FromInternalValue(statement.ColumnInt64(1));
    758     base::Time created_time = last_visit_time;
    759 
    760     if (statement.ColumnType(2) != sql::COLUMN_TYPE_NULL)
    761       created_time = base::Time::FromInternalValue(statement.ColumnInt64(2));
    762 
    763     if (!history_db_->AddBookmarkCacheRow(created_time, last_visit_time,
    764                                           statement.ColumnInt64(0)))
    765       return false;
    766   }
    767   return true;
    768 }
    769 
    770 bool AndroidProviderBackend::UpdateRemovedURLs() {
    771   return history_db_->DeleteUnusedAndroidURLs();
    772 }
    773 
    774 bool AndroidProviderBackend::UpdateBookmarks() {
    775   if (history_client_ == NULL) {
    776     LOG(ERROR) << "HistoryClient is not available";
    777     return false;
    778   }
    779 
    780   std::vector<URLAndTitle> bookmarks;
    781   history_client_->GetBookmarks(&bookmarks);
    782 
    783   if (bookmarks.empty())
    784     return true;
    785 
    786   std::vector<URLID> url_ids;
    787   for (std::vector<URLAndTitle>::const_iterator i =
    788            bookmarks.begin(); i != bookmarks.end(); ++i) {
    789     URLID url_id = history_db_->GetRowForURL(i->url, NULL);
    790     if (url_id == 0) {
    791       URLRow url_row(i->url);
    792       url_row.set_title(i->title);
    793       // Set the visit time to the UnixEpoch since that's when the Android
    794       // system time starts. The Android have a CTS testcase for this.
    795       url_row.set_last_visit(base::Time::UnixEpoch());
    796       url_row.set_hidden(true);
    797       url_id = history_db_->AddURL(url_row);
    798       if (url_id == 0) {
    799         LOG(ERROR) << "Can not add url for the new bookmark";
    800         return false;
    801       }
    802       if (!history_db_->AddAndroidURLRow(i->url.spec(), url_id))
    803         return false;
    804       if (!history_db_->AddBookmarkCacheRow(base::Time::UnixEpoch(),
    805                                             base::Time::UnixEpoch(), url_id))
    806         return false;
    807     }
    808     url_ids.push_back(url_id);
    809   }
    810 
    811   return history_db_->MarkURLsAsBookmarked(url_ids);
    812 }
    813 
    814 bool AndroidProviderBackend::UpdateFavicon() {
    815   ThumbnailDatabase::IconMappingEnumerator enumerator;
    816 
    817   // We want the AndroidProviderBackend run without thumbnail_db_
    818   if (!thumbnail_db_)
    819     return true;
    820 
    821   if (!thumbnail_db_->InitIconMappingEnumerator(favicon_base::FAVICON,
    822                                                 &enumerator))
    823     return false;
    824 
    825   IconMapping icon_mapping;
    826   while (enumerator.GetNextIconMapping(&icon_mapping)) {
    827     URLID url_id = history_db_->GetRowForURL(icon_mapping.page_url, NULL);
    828     if (url_id == 0) {
    829       LOG(ERROR) << "Can not find favicon's page url";
    830       continue;
    831     }
    832     history_db_->SetFaviconID(url_id, icon_mapping.icon_id);
    833   }
    834   return true;
    835 }
    836 
    837 bool AndroidProviderBackend::UpdateSearchTermTable() {
    838   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE,
    839                                                    kSearchTermUpdateClause));
    840   while (statement.Step()) {
    841     base::string16 term = statement.ColumnString16(0);
    842     base::Time last_visit_time =
    843         base::Time::FromInternalValue(statement.ColumnInt64(1));
    844     SearchTermRow search_term_row;
    845     if (history_db_->GetSearchTerm(term, &search_term_row)) {
    846       if (search_term_row.last_visit_time != last_visit_time) {
    847         search_term_row.last_visit_time = last_visit_time;
    848         if (!history_db_->UpdateSearchTerm(search_term_row.id, search_term_row))
    849           return false;
    850       }
    851     } else {
    852       if (!history_db_->AddSearchTerm(term, last_visit_time))
    853         return false;
    854     }
    855   }
    856   if (!history_db_->DeleteUnusedSearchTerms())
    857     return false;
    858 
    859   return true;
    860 }
    861 
    862 int AndroidProviderBackend::AppendBookmarkResultColumn(
    863     const std::vector<HistoryAndBookmarkRow::ColumnID>& projections,
    864     std::string* result_column) {
    865   int replaced_index = -1;
    866   // Attach the projections
    867   bool first = true;
    868   int index = 0;
    869   for (std::vector<HistoryAndBookmarkRow::ColumnID>::const_iterator i =
    870            projections.begin(); i != projections.end(); ++i) {
    871     if (first)
    872       first = false;
    873     else
    874       result_column->append(", ");
    875 
    876     if (*i == HistoryAndBookmarkRow::FAVICON)
    877       replaced_index = index;
    878 
    879     result_column->append(HistoryAndBookmarkRow::GetAndroidName(*i));
    880     index++;
    881   }
    882   return replaced_index;
    883 }
    884 
    885 bool AndroidProviderBackend::GetSelectedURLs(
    886     const std::string& selection,
    887     const std::vector<base::string16>& selection_args,
    888     TableIDRows* rows) {
    889   std::string sql("SELECT url_id, urls_url, bookmark FROM (");
    890   sql.append(kVirtualHistoryAndBookmarkTable);
    891   sql.append(" )");
    892 
    893   if (!selection.empty()) {
    894     sql.append(" WHERE ");
    895     sql.append(selection);
    896   }
    897 
    898   sql::Statement statement(db_->GetUniqueStatement(sql.c_str()));
    899   int count = 0;
    900   BindStatement(selection_args, &statement, &count);
    901   if (!statement.is_valid()) {
    902     LOG(ERROR) << db_->GetErrorMessage();
    903     return false;
    904   }
    905   while (statement.Step()) {
    906     TableIDRow row;
    907     row.url_id = statement.ColumnInt64(0);
    908     row.url = GURL(statement.ColumnString(1));
    909     row.bookmarked = statement.ColumnBool(2);
    910     rows->push_back(row);
    911   }
    912   return true;
    913 }
    914 
    915 bool AndroidProviderBackend::GetSelectedSearchTerms(
    916     const std::string& selection,
    917     const std::vector<base::string16>& selection_args,
    918     SearchTerms* rows) {
    919   std::string sql("SELECT search "
    920                   "FROM android_cache_db.search_terms ");
    921   if (!selection.empty()) {
    922     sql.append(" WHERE ");
    923     sql.append(selection);
    924   }
    925   sql::Statement statement(db_->GetUniqueStatement(sql.c_str()));
    926   int count = 0;
    927   BindStatement(selection_args, &statement, &count);
    928   if (!statement.is_valid()) {
    929     LOG(ERROR) << db_->GetErrorMessage();
    930     return false;
    931   }
    932   while (statement.Step()) {
    933     rows->push_back(statement.ColumnString16(0));
    934   }
    935   return true;
    936 }
    937 
    938 void AndroidProviderBackend::AppendSearchResultColumn(
    939     const std::vector<SearchRow::ColumnID>& projections,
    940     std::string* result_column) {
    941   bool first = true;
    942   int index = 0;
    943   for (std::vector<SearchRow::ColumnID>::const_iterator i =
    944            projections.begin(); i != projections.end(); ++i) {
    945     if (first)
    946       first = false;
    947     else
    948       result_column->append(", ");
    949 
    950     result_column->append(SearchRow::GetAndroidName(*i));
    951     index++;
    952   }
    953 }
    954 
    955 bool AndroidProviderBackend::SimulateUpdateURL(
    956     const HistoryAndBookmarkRow& row,
    957     const TableIDRows& ids,
    958     HistoryNotifications* notifications) {
    959   DCHECK(ids.size() == 1);
    960   // URL can not be updated, we simulate the update by deleting the old URL
    961   // and inserting the new one; We do update the android_urls table as the id
    962   // need to keep same.
    963 
    964   // Find all columns value of the current URL.
    965   std::vector<HistoryAndBookmarkRow::ColumnID> projections;
    966   projections.push_back(HistoryAndBookmarkRow::LAST_VISIT_TIME);
    967   projections.push_back(HistoryAndBookmarkRow::CREATED);
    968   projections.push_back(HistoryAndBookmarkRow::VISIT_COUNT);
    969   projections.push_back(HistoryAndBookmarkRow::TITLE);
    970   projections.push_back(HistoryAndBookmarkRow::FAVICON);
    971   projections.push_back(HistoryAndBookmarkRow::BOOKMARK);
    972 
    973   std::ostringstream oss;
    974   oss << "url_id = " << ids[0].url_id;
    975 
    976   scoped_ptr<AndroidStatement> statement;
    977   statement.reset(QueryHistoryAndBookmarksInternal(projections, oss.str(),
    978       std::vector<base::string16>(), std::string()));
    979   if (!statement.get() || !statement->statement()->Step())
    980     return false;
    981 
    982   HistoryAndBookmarkRow new_row;
    983   new_row.set_last_visit_time(FromDatabaseTime(
    984       statement->statement()->ColumnInt64(0)));
    985   new_row.set_created(FromDatabaseTime(
    986       statement->statement()->ColumnInt64(1)));
    987   new_row.set_visit_count(statement->statement()->ColumnInt(2));
    988   new_row.set_title(statement->statement()->ColumnString16(3));
    989 
    990   scoped_ptr<URLsDeletedDetails> deleted_details(new URLsDeletedDetails);
    991   scoped_ptr<std::set<GURL> > favicons(new std::set<GURL>);
    992   scoped_ptr<URLsModifiedDetails> modified(new URLsModifiedDetails);
    993   URLRow old_url_row;
    994   if (!history_db_->GetURLRow(ids[0].url_id, &old_url_row))
    995     return false;
    996   deleted_details->rows.push_back(old_url_row);
    997 
    998   favicon_base::FaviconID favicon_id = statement->statement()->ColumnInt64(4);
    999   if (favicon_id) {
   1000     std::vector<FaviconBitmap> favicon_bitmaps;
   1001     if (!thumbnail_db_ ||
   1002         !thumbnail_db_->GetFaviconBitmaps(favicon_id, &favicon_bitmaps))
   1003       return false;
   1004    scoped_refptr<base::RefCountedMemory> bitmap_data =
   1005        favicon_bitmaps[0].bitmap_data;
   1006    if (bitmap_data.get() && bitmap_data->size())
   1007       new_row.set_favicon(bitmap_data);
   1008    favicons->insert(old_url_row.url());
   1009    favicons->insert(row.url());
   1010   }
   1011   new_row.set_is_bookmark(statement->statement()->ColumnBool(5));
   1012 
   1013   // The SQLHandler vector is not used here because the row in android_url
   1014   // shouldn't be deleted, we need keep the AndroidUIID unchanged, so it
   1015   // appears update to the client.
   1016   if (!urls_handler_->Delete(ids))
   1017     return false;
   1018 
   1019   if (!visit_handler_->Delete(ids))
   1020     return false;
   1021 
   1022   if (favicon_handler_ && !favicon_handler_->Delete(ids))
   1023     return false;
   1024 
   1025   if (!bookmark_model_handler_->Delete(ids))
   1026     return false;
   1027 
   1028   new_row.set_url(row.url());
   1029   new_row.set_raw_url(row.raw_url());
   1030   if (row.is_value_set_explicitly(HistoryAndBookmarkRow::LAST_VISIT_TIME))
   1031     new_row.set_last_visit_time(row.last_visit_time());
   1032   if (row.is_value_set_explicitly(HistoryAndBookmarkRow::CREATED))
   1033     new_row.set_created(row.created());
   1034   if (row.is_value_set_explicitly(HistoryAndBookmarkRow::VISIT_COUNT))
   1035     new_row.set_visit_count(row.visit_count());
   1036   if (row.is_value_set_explicitly(HistoryAndBookmarkRow::TITLE))
   1037     new_row.set_title(row.title());
   1038   if (row.is_value_set_explicitly(HistoryAndBookmarkRow::FAVICON)) {
   1039     new_row.set_favicon(row.favicon());
   1040     favicons->insert(new_row.url());
   1041   }
   1042   if (row.is_value_set_explicitly(HistoryAndBookmarkRow::BOOKMARK))
   1043     new_row.set_is_bookmark(row.is_bookmark());
   1044 
   1045   if (!urls_handler_->Insert(&new_row))
   1046     return false;
   1047 
   1048   if (!visit_handler_->Insert(&new_row))
   1049     return false;
   1050 
   1051   // Update the current row instead of inserting a new row in android urls
   1052   // table. We need keep the AndroidUIID unchanged, so it appears update
   1053   // to the client.
   1054   if (!android_urls_handler_->Update(new_row, ids))
   1055     return false;
   1056 
   1057   if (favicon_handler_ && !favicon_handler_->Insert(&new_row))
   1058     return false;
   1059 
   1060   if (!bookmark_model_handler_->Insert(&new_row))
   1061     return false;
   1062 
   1063   URLRow new_url_row;
   1064   if (!history_db_->GetURLRow(new_row.url_id(), &new_url_row))
   1065     return false;
   1066 
   1067   modified->changed_urls.push_back(new_url_row);
   1068 
   1069   scoped_ptr<HistoryDetails> details = deleted_details.PassAs<HistoryDetails>();
   1070   notifications->push_back(
   1071       base::Bind(&HistoryBackend::Delegate::BroadcastNotifications,
   1072                  base::Unretained(delegate_),
   1073                  chrome::NOTIFICATION_HISTORY_URLS_DELETED,
   1074                  base::Passed(&details)));
   1075   if (favicons && !favicons->empty()) {
   1076     notifications->push_back(base::Bind(&RunNotifyFaviconChanged,
   1077                                         base::Unretained(delegate_),
   1078                                         base::Passed(&favicons)));
   1079   }
   1080   scoped_ptr<HistoryDetails> other_details = modified.PassAs<HistoryDetails>();
   1081   notifications->push_back(
   1082       base::Bind(&HistoryBackend::Delegate::BroadcastNotifications,
   1083                  base::Unretained(delegate_),
   1084                  chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
   1085                  base::Passed(&other_details)));
   1086 
   1087   return true;
   1088 }
   1089 
   1090 AndroidStatement* AndroidProviderBackend::QueryHistoryAndBookmarksInternal(
   1091     const std::vector<HistoryAndBookmarkRow::ColumnID>& projections,
   1092     const std::string& selection,
   1093     const std::vector<base::string16>& selection_args,
   1094     const std::string& sort_order) {
   1095   std::string sql;
   1096   sql.append("SELECT ");
   1097   int replaced_index = AppendBookmarkResultColumn(projections, &sql);
   1098   sql.append(" FROM (");
   1099   sql.append(kVirtualHistoryAndBookmarkTable);
   1100   sql.append(")");
   1101 
   1102   if (!selection.empty()) {
   1103     sql.append(" WHERE ");
   1104     sql.append(selection);
   1105   }
   1106 
   1107   if (!sort_order.empty()) {
   1108     sql.append(" ORDER BY ");
   1109     sql.append(sort_order);
   1110   }
   1111 
   1112   scoped_ptr<sql::Statement> statement(new sql::Statement(
   1113       db_->GetUniqueStatement(sql.c_str())));
   1114   int count = 0;
   1115   BindStatement(selection_args, statement.get(), &count);
   1116   if (!statement->is_valid()) {
   1117     LOG(ERROR) << db_->GetErrorMessage();
   1118     return NULL;
   1119   }
   1120   sql::Statement* result = statement.release();
   1121   return new AndroidStatement(result, replaced_index);
   1122 }
   1123 
   1124 bool AndroidProviderBackend::DeleteHistoryInternal(
   1125     const TableIDRows& urls,
   1126     bool delete_bookmarks,
   1127     HistoryNotifications* notifications) {
   1128   scoped_ptr<URLsDeletedDetails> deleted_details(new URLsDeletedDetails);
   1129   scoped_ptr<std::set<GURL> > favicon(new std::set<GURL>);
   1130   for (TableIDRows::const_iterator i = urls.begin(); i != urls.end(); ++i) {
   1131     URLRow url_row;
   1132     if (!history_db_->GetURLRow(i->url_id, &url_row))
   1133       return false;
   1134     deleted_details->rows.push_back(url_row);
   1135     if (thumbnail_db_ &&
   1136         thumbnail_db_->GetIconMappingsForPageURL(url_row.url(), NULL))
   1137       favicon->insert(url_row.url());
   1138   }
   1139 
   1140   // Only invoke Delete on the BookmarkModelHandler if we need
   1141   // to delete bookmarks.
   1142   for (std::vector<SQLHandler*>::iterator i =
   1143        sql_handlers_.begin(); i != sql_handlers_.end(); ++i) {
   1144     if ((*i) != bookmark_model_handler_.get() || delete_bookmarks)
   1145       if (!(*i)->Delete(urls))
   1146         return false;
   1147   }
   1148 
   1149   scoped_ptr<HistoryDetails> details = deleted_details.PassAs<HistoryDetails>();
   1150   notifications->push_back(
   1151       base::Bind(&HistoryBackend::Delegate::BroadcastNotifications,
   1152                  base::Unretained(delegate_),
   1153                  chrome::NOTIFICATION_HISTORY_URLS_DELETED,
   1154                  base::Passed(&details)));
   1155   if (favicon && !favicon->empty()) {
   1156     notifications->push_back(base::Bind(&RunNotifyFaviconChanged,
   1157                                         base::Unretained(delegate_),
   1158                                         base::Passed(&favicon)));
   1159   }
   1160   return true;
   1161 }
   1162 
   1163 void AndroidProviderBackend::BroadcastNotifications(
   1164     HistoryNotifications* notifications) {
   1165   while (!notifications->empty()) {
   1166     notifications->back().Run();
   1167     notifications->pop_back();
   1168   }
   1169 }
   1170 
   1171 bool AndroidProviderBackend::AddSearchTerm(const SearchRow& values) {
   1172   DCHECK(values.is_value_set_explicitly(SearchRow::SEARCH_TERM));
   1173   DCHECK(values.is_value_set_explicitly(SearchRow::KEYWORD_ID));
   1174   DCHECK(values.is_value_set_explicitly(SearchRow::URL));
   1175 
   1176   URLRow url_row;
   1177   HistoryAndBookmarkRow bookmark_row;
   1178   // Android CTS test BrowserTest.testAccessSearches allows insert the same
   1179   // seach term multiple times, and just search time need updated.
   1180   if (history_db_->GetRowForURL(values.url(), &url_row)) {
   1181     // Already exist, Add a visit.
   1182     if (values.is_value_set_explicitly(SearchRow::SEARCH_TIME))
   1183       bookmark_row.set_last_visit_time(values.search_time());
   1184     else
   1185       bookmark_row.set_visit_count(url_row.visit_count() + 1);
   1186     TableIDRows table_id_rows;
   1187     TableIDRow table_id_row;
   1188     table_id_row.url = values.url();
   1189     table_id_row.url_id = url_row.id();
   1190     table_id_rows.push_back(table_id_row);
   1191     if (!urls_handler_->Update(bookmark_row, table_id_rows))
   1192       return false;
   1193     if (!visit_handler_->Update(bookmark_row, table_id_rows))
   1194       return false;
   1195 
   1196     if (!history_db_->GetKeywordSearchTermRow(url_row.id(), NULL))
   1197       if (!history_db_->SetKeywordSearchTermsForURL(url_row.id(),
   1198                values.keyword_id(), values.search_term()))
   1199         return false;
   1200   } else {
   1201     bookmark_row.set_raw_url(values.url().spec());
   1202     bookmark_row.set_url(values.url());
   1203     if (values.is_value_set_explicitly(SearchRow::SEARCH_TIME))
   1204       bookmark_row.set_last_visit_time(values.search_time());
   1205 
   1206     if (!urls_handler_->Insert(&bookmark_row))
   1207       return false;
   1208 
   1209     if (!visit_handler_->Insert(&bookmark_row))
   1210       return false;
   1211 
   1212     if (!android_urls_handler_->Insert(&bookmark_row))
   1213       return false;
   1214 
   1215     if (!history_db_->SetKeywordSearchTermsForURL(bookmark_row.url_id(),
   1216                           values.keyword_id(), values.search_term()))
   1217       return false;
   1218   }
   1219   return true;
   1220 }
   1221 
   1222 }  // namespace history
   1223