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