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 ¬ifications))
263 return false;
264
265 transaction.Commit();
266 BroadcastNotifications(¬ifications);
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, ¬ifications);
277 if (!id)
278 return 0;
279
280 transaction.Commit();
281 BroadcastNotifications(¬ifications);
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 ¬ifications))
295 return false;
296
297 transaction.Commit();
298 BroadcastNotifications(¬ifications);
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, ¬ifications))
311 return false;
312
313 transaction.Commit();
314 BroadcastNotifications(¬ifications);
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