• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BindStatement(const std::vector<base::string16> & selection_args,sql::Statement * statement,int * col_index)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 
IsHistoryAndBookmarkRowValid(const HistoryAndBookmarkRow & row)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 
HistoryNotifications()122 AndroidProviderBackend::HistoryNotifications::HistoryNotifications() {
123 }
124 
~HistoryNotifications()125 AndroidProviderBackend::HistoryNotifications::~HistoryNotifications() {
126 }
127 
PushBack(int type,scoped_ptr<HistoryDetails> detail)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 
PopBackType()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>
PopBackDetails()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 
ScopedTransaction(HistoryDatabase * history_db,ThumbnailDatabase * thumbnail_db)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 
~ScopedTransaction()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 
Commit()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 
AndroidProviderBackend(const base::FilePath & db_name,HistoryDatabase * history_db,ThumbnailDatabase * thumbnail_db,HistoryClient * history_client,HistoryBackend::Delegate * delegate)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 
~AndroidProviderBackend()230 AndroidProviderBackend::~AndroidProviderBackend() {
231 }
232 
QueryHistoryAndBookmarks(const std::vector<HistoryAndBookmarkRow::ColumnID> & projections,const std::string & selection,const std::vector<base::string16> & selection_args,const std::string & sort_order)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 
UpdateHistoryAndBookmarks(const HistoryAndBookmarkRow & row,const std::string & selection,const std::vector<base::string16> & selection_args,int * updated_count)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 
InsertHistoryAndBookmark(const HistoryAndBookmarkRow & values)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 
DeleteHistoryAndBookmarks(const std::string & selection,const std::vector<base::string16> & selection_args,int * deleted_count)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 
DeleteHistory(const std::string & selection,const std::vector<base::string16> & selection_args,int * deleted_count)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 
UpdateHistoryAndBookmarks(const HistoryAndBookmarkRow & row,const std::string & selection,const std::vector<base::string16> & selection_args,int * updated_count,HistoryNotifications * notifications)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 
InsertHistoryAndBookmark(const HistoryAndBookmarkRow & values,bool ensure_initialized_and_updated,HistoryNotifications * notifications)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 
DeleteHistoryAndBookmarks(const std::string & selection,const std::vector<base::string16> & selection_args,int * deleted_count,HistoryNotifications * notifications)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 
DeleteHistory(const std::string & selection,const std::vector<base::string16> & selection_args,int * deleted_count,HistoryNotifications * notifications)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 
QuerySearchTerms(const std::vector<SearchRow::ColumnID> & projections,const std::string & selection,const std::vector<base::string16> & selection_args,const std::string & sort_order)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 
UpdateSearchTerms(const SearchRow & row,const std::string & selection,const std::vector<base::string16> & selection_args,int * update_count)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 
InsertSearchTerm(const SearchRow & values)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 
DeleteSearchTerms(const std::string & selection,const std::vector<base::string16> & selection_args,int * deleted_count)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 
EnsureInitializedAndUpdated()687 bool AndroidProviderBackend::EnsureInitializedAndUpdated() {
688   if (!initialized_) {
689     if (!Init())
690       return false;
691   }
692   return UpdateTables();
693 }
694 
Init()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 
UpdateTables()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 
UpdateVisitedURLs()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 
UpdateRemovedURLs()791 bool AndroidProviderBackend::UpdateRemovedURLs() {
792   return history_db_->DeleteUnusedAndroidURLs();
793 }
794 
UpdateBookmarks()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 
UpdateFavicon()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 
UpdateSearchTermTable()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 
AppendBookmarkResultColumn(const std::vector<HistoryAndBookmarkRow::ColumnID> & projections,std::string * result_column)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 
GetSelectedURLs(const std::string & selection,const std::vector<base::string16> & selection_args,TableIDRows * rows)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 
GetSelectedSearchTerms(const std::string & selection,const std::vector<base::string16> & selection_args,SearchTerms * rows)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 
AppendSearchResultColumn(const std::vector<SearchRow::ColumnID> & projections,std::string * result_column)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 
SimulateUpdateURL(const HistoryAndBookmarkRow & row,const TableIDRows & ids,HistoryNotifications * notifications)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 
QueryHistoryAndBookmarksInternal(const std::vector<HistoryAndBookmarkRow::ColumnID> & projections,const std::string & selection,const std::vector<base::string16> & selection_args,const std::string & sort_order)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 
DeleteHistoryInternal(const TableIDRows & urls,bool delete_bookmarks,HistoryNotifications * notifications)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 
BroadcastNotifications(HistoryNotifications * notifications)1170 void AndroidProviderBackend::BroadcastNotifications(
1171     HistoryNotifications* notifications) {
1172   while (!notifications->empty()) {
1173     delegate_->BroadcastNotifications(notifications->PopBackType(),
1174                                       notifications->PopBackDetails());
1175   }
1176 }
1177 
AddSearchTerm(const SearchRow & values)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