diff options
author | zork@chromium.org <zork@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-30 17:33:18 +0000 |
---|---|---|
committer | zork@chromium.org <zork@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-30 17:33:18 +0000 |
commit | 7e6851a56fbeef097a06e58e0aa22ed78fb3b0eb (patch) | |
tree | 706a132fa94804b3d8df4d51a8b7af8ae4c3743e | |
parent | 1cd5881de55d7b557e7e308b9b2e7a2442c86a6e (diff) | |
download | chromium_src-7e6851a56fbeef097a06e58e0aa22ed78fb3b0eb.zip chromium_src-7e6851a56fbeef097a06e58e0aa22ed78fb3b0eb.tar.gz chromium_src-7e6851a56fbeef097a06e58e0aa22ed78fb3b0eb.tar.bz2 |
Update typed_url sync to support visits.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/1775004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46085 0039d316-1c4b-4281-b951-d872f2087c98
9 files changed, 602 insertions, 127 deletions
diff --git a/chrome/browser/history/history_backend.cc b/chrome/browser/history/history_backend.cc index 58fa489..4be006a 100644 --- a/chrome/browser/history/history_backend.cc +++ b/chrome/browser/history/history_backend.cc @@ -855,12 +855,65 @@ bool HistoryBackend::GetAllTypedURLs(std::vector<history::URLRow>* urls) { return false; } -bool HistoryBackend::UpdateURL(const URLID id, const history::URLRow& url) { +bool HistoryBackend::GetVisitsForURL(URLID id, VisitVector* visits) { + if (db_.get()) + return db_->GetVisitsForURL(id, visits); + return false; +} + +bool HistoryBackend::UpdateURL(URLID id, const history::URLRow& url) { if (db_.get()) return db_->UpdateURLRow(id, url); return false; } +bool HistoryBackend::AddVisits(const GURL& url, + const std::vector<base::Time>& visits) { + if (db_.get()) { + for (std::vector<base::Time>::const_iterator visit = visits.begin(); + visit != visits.end(); ++visit) { + if (!AddPageVisit(url, *visit, 0, 0).first) { + return false; + } + } + ScheduleCommit(); + return true; + } + return false; +} + +bool HistoryBackend::RemoveVisits(const VisitVector& visits) { + if (db_.get()) { + std::map<URLID, int> url_visits_removed; + for (VisitVector::const_iterator visit = visits.begin(); + visit != visits.end(); ++visit) { + db_->DeleteVisit(*visit); + std::map<URLID, int>::iterator visit_count = + url_visits_removed.find(visit->url_id); + if (visit_count == url_visits_removed.end()) { + url_visits_removed[visit->url_id] = 1; + } else { + ++visit_count->second; + } + } + for (std::map<URLID, int>::iterator count = url_visits_removed.begin(); + count != url_visits_removed.end(); ++count) { + history::URLRow url_row; + if (!db_->GetURLRow(count->first, &url_row)) { + return false; + } + DCHECK(count->second <= url_row.visit_count()); + url_row.set_visit_count(url_row.visit_count() - count->second); + if (!db_->UpdateURLRow(url_row.id(), url_row)) { + return false; + } + } + ScheduleCommit(); + return true; + } + return false; +} + bool HistoryBackend::GetURL(const GURL& url, history::URLRow* url_row) { if (db_.get()) return db_->GetRowForURL(url, url_row) != 0; diff --git a/chrome/browser/history/history_backend.h b/chrome/browser/history/history_backend.h index 7d230c6..7289b5a 100644 --- a/chrome/browser/history/history_backend.h +++ b/chrome/browser/history/history_backend.h @@ -240,7 +240,14 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>, virtual bool GetAllTypedURLs(std::vector<history::URLRow>* urls); - virtual bool UpdateURL(const URLID id, const history::URLRow& url); + virtual bool GetVisitsForURL(URLID id, VisitVector* visits); + + virtual bool UpdateURL(URLID id, const history::URLRow& url); + + virtual bool AddVisits(const GURL& url, + const std::vector<base::Time>& visits); + + virtual bool RemoveVisits(const VisitVector& visits); virtual bool GetURL(const GURL& url, history::URLRow* url_row); diff --git a/chrome/browser/sync/glue/typed_url_change_processor.cc b/chrome/browser/sync/glue/typed_url_change_processor.cc index 864721b..e659f23 100644 --- a/chrome/browser/sync/glue/typed_url_change_processor.cc +++ b/chrome/browser/sync/glue/typed_url_change_processor.cc @@ -50,20 +50,34 @@ void TypedUrlChangeProcessor::Observe(NotificationType type, LOG(INFO) << "Observed typed_url change."; DCHECK(running()); DCHECK(NotificationType::HISTORY_TYPED_URLS_MODIFIED == type || - NotificationType::HISTORY_URLS_DELETED == type); + NotificationType::HISTORY_URLS_DELETED == type || + NotificationType::HISTORY_URL_VISITED == type); if (type == NotificationType::HISTORY_TYPED_URLS_MODIFIED) { HandleURLsModified(Details<history::URLsModifiedDetails>(details).ptr()); } else if (type == NotificationType::HISTORY_URLS_DELETED) { HandleURLsDeleted(Details<history::URLsDeletedDetails>(details).ptr()); + } else if (type == NotificationType::HISTORY_URL_VISITED) { + HandleURLsVisited(Details<history::URLVisitedDetails>(details).ptr()); } } void TypedUrlChangeProcessor::HandleURLsModified( history::URLsModifiedDetails* details) { - sync_api::WriteTransaction trans(share_handle()); + // Get all the visits. + std::map<history::URLID, history::VisitVector> visit_vectors; + for (std::vector<history::URLRow>::iterator url = + details->changed_urls.begin(); url != details->changed_urls.end(); + ++url) { + if (!history_backend_->GetVisitsForURL(url->id(), + &(visit_vectors[url->id()]))) { + error_handler()->OnUnrecoverableError(); + LOG(ERROR) << "Could not get the url's visits."; + return; + } + DCHECK(!visit_vectors[url->id()].empty()); + } - // TODO(sync): Get visits without holding the write transaction. - // See issue 34206 + sync_api::WriteTransaction trans(share_handle()); sync_api::ReadNode typed_url_root(&trans); if (!typed_url_root.InitByTagLookup(kTypedUrlTag)) { @@ -78,9 +92,20 @@ void TypedUrlChangeProcessor::HandleURLsModified( ++url) { std::string tag = url->url().spec(); + history::VisitVector& visits = visit_vectors[url->id()]; + + DCHECK(!visits.empty()); + + DCHECK(static_cast<size_t>(url->visit_count()) == visits.size()); + if (static_cast<size_t>(url->visit_count()) != visits.size()) { + error_handler()->OnUnrecoverableError(); + LOG(ERROR) << "Visit count does not match."; + return; + } + sync_api::WriteNode update_node(&trans); if (update_node.InitByClientTagLookup(syncable::TYPED_URLS, tag)) { - model_associator_->WriteToSyncNode(*url, &update_node); + model_associator_->WriteToSyncNode(*url, visits, &update_node); } else { sync_api::WriteNode create_node(&trans); if (!create_node.InitUniqueByCreation(syncable::TYPED_URLS, @@ -91,7 +116,7 @@ void TypedUrlChangeProcessor::HandleURLsModified( } create_node.SetTitle(UTF8ToWide(tag)); - model_associator_->WriteToSyncNode(*url, &create_node); + model_associator_->WriteToSyncNode(*url, visits, &create_node); model_associator_->Associate(&tag, create_node.GetId()); } @@ -112,13 +137,8 @@ void TypedUrlChangeProcessor::HandleURLsDeleted( url != details->urls.end(); ++url) { sync_api::WriteNode sync_node(&trans); int64 sync_id = - model_associator_->GetSyncIdFromChromeId(url->spec()); - if (sync_api::kInvalidId == sync_id) { - LOG(ERROR) << "Unexpected notification for: " << - url->spec(); - error_handler()->OnUnrecoverableError(); - return; - } else { + model_associator_->GetSyncIdFromChromeId(url->spec()); + if (sync_api::kInvalidId != sync_id) { if (!sync_node.InitByIdLookup(sync_id)) { LOG(ERROR) << "Typed url node lookup failed."; error_handler()->OnUnrecoverableError(); @@ -131,6 +151,34 @@ void TypedUrlChangeProcessor::HandleURLsDeleted( } } +void TypedUrlChangeProcessor::HandleURLsVisited( + history::URLVisitedDetails* details) { + if (!details->row.typed_count()) { + // We only care about typed urls. + return; + } + history::VisitVector visits; + if (!history_backend_->GetVisitsForURL(details->row.id(), &visits) || + visits.empty()) { + error_handler()->OnUnrecoverableError(); + LOG(ERROR) << "Could not get the url's visits."; + return; + } + + DCHECK(static_cast<size_t>(details->row.visit_count()) == visits.size()); + + sync_api::WriteTransaction trans(share_handle()); + std::string tag = details->row.url().spec(); + sync_api::WriteNode update_node(&trans); + if (!update_node.InitByClientTagLookup(syncable::TYPED_URLS, tag)) { + // If we don't know about it yet, it will be added later. + return; + } + sync_pb::TypedUrlSpecifics typed_url(update_node.GetTypedUrlSpecifics()); + typed_url.add_visit(visits.back().visit_time.ToInternalValue()); + update_node.SetTypedUrlSpecifics(typed_url); +} + void TypedUrlChangeProcessor::ApplyChangesFromSyncModel( const sync_api::BaseTransaction* trans, const sync_api::SyncManager::ChangeRecord* changes, @@ -149,6 +197,8 @@ void TypedUrlChangeProcessor::ApplyChangesFromSyncModel( TypedUrlModelAssociator::TypedUrlTitleVector titles; TypedUrlModelAssociator::TypedUrlVector new_urls; + TypedUrlModelAssociator::TypedUrlVisitVector new_visits; + history::VisitVector deleted_visits; TypedUrlModelAssociator::TypedUrlUpdateVector updated_urls; for (int i = 0; i < change_count; ++i) { @@ -166,33 +216,62 @@ void TypedUrlChangeProcessor::ApplyChangesFromSyncModel( const sync_pb::TypedUrlSpecifics& typed_url( sync_node.GetTypedUrlSpecifics()); + GURL url(typed_url.url()); + if (sync_api::SyncManager::ChangeRecord::ACTION_ADD == changes[i].action) { - history::URLRow new_url(GURL(typed_url.url())); + DCHECK(typed_url.visit_size()); + if (!typed_url.visit_size()) { + continue; + } + + history::URLRow new_url(url); new_url.set_title(UTF8ToWide(typed_url.title())); - new_url.set_visit_count(typed_url.visit_count()); + + // When we add a new url, the last visit is always added, thus we set + // the initial visit count to one. This value will be automatically + // incremented as visits are added. + new_url.set_visit_count(1); new_url.set_typed_count(typed_url.typed_count()); - new_url.set_last_visit( - base::Time::FromInternalValue(typed_url.last_visit())); new_url.set_hidden(typed_url.hidden()); + new_url.set_last_visit(base::Time::FromInternalValue( + typed_url.visit(typed_url.visit_size() - 1))); + new_urls.push_back(new_url); + + // The latest visit gets added automatically, so skip it. + std::vector<base::Time> added_visits; + for (int c = 0; c < typed_url.visit_size() - 1; ++c) { + DCHECK(typed_url.visit(c) < typed_url.visit(c + 1)); + added_visits.push_back( + base::Time::FromInternalValue(typed_url.visit(c))); + } + + new_visits.push_back( + std::pair<GURL, std::vector<base::Time> >(url, added_visits)); } else if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE == changes[i].action) { - history_backend_->DeleteURL(GURL(typed_url.url())); + history_backend_->DeleteURL(url); } else { history::URLRow old_url; - if (!history_backend_->GetURL(GURL(typed_url.url()), &old_url)) { + if (!history_backend_->GetURL(url, &old_url)) { LOG(ERROR) << "TypedUrl db lookup failed."; error_handler()->OnUnrecoverableError(); return; } - history::URLRow new_url(GURL(typed_url.url())); + history::VisitVector visits; + if (!history_backend_->GetVisitsForURL(old_url.id(), &visits)) { + LOG(ERROR) << "Could not get the url's visits."; + error_handler()->OnUnrecoverableError(); + return; + } + + history::URLRow new_url(url); new_url.set_title(UTF8ToWide(typed_url.title())); - new_url.set_visit_count(typed_url.visit_count()); + new_url.set_visit_count(old_url.visit_count()); new_url.set_typed_count(typed_url.typed_count()); - new_url.set_last_visit( - base::Time::FromInternalValue(typed_url.last_visit())); + new_url.set_last_visit(old_url.last_visit()); new_url.set_hidden(typed_url.hidden()); updated_urls.push_back( @@ -202,9 +281,28 @@ void TypedUrlChangeProcessor::ApplyChangesFromSyncModel( titles.push_back(std::pair<GURL, std::wstring>(new_url.url(), new_url.title())); } + + std::vector<base::Time> added_visits; + history::VisitVector removed_visits; + TypedUrlModelAssociator::DiffVisits(visits, typed_url, + &added_visits, &removed_visits); + if (added_visits.size()) { + new_visits.push_back( + std::pair<GURL, std::vector<base::Time> >(url, added_visits)); + } + if (removed_visits.size()) { + deleted_visits.insert(deleted_visits.end(), removed_visits.begin(), + removed_visits.end()); + } } } - model_associator_->WriteToHistoryBackend(&titles, &new_urls, &updated_urls); + if (!model_associator_->WriteToHistoryBackend(&titles, &new_urls, + &updated_urls, + &new_visits, &deleted_visits)) { + LOG(ERROR) << "Could not write to the history backend."; + error_handler()->OnUnrecoverableError(); + return; + } StartObserving(); } @@ -227,6 +325,8 @@ void TypedUrlChangeProcessor::StartObserving() { NotificationService::AllSources()); notification_registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED, NotificationService::AllSources()); + notification_registrar_.Add(this, NotificationType::HISTORY_URL_VISITED, + NotificationService::AllSources()); } void TypedUrlChangeProcessor::StopObserving() { @@ -237,6 +337,9 @@ void TypedUrlChangeProcessor::StopObserving() { notification_registrar_.Remove(this, NotificationType::HISTORY_URLS_DELETED, NotificationService::AllSources()); + notification_registrar_.Remove(this, + NotificationType::HISTORY_URL_VISITED, + NotificationService::AllSources()); } } // namespace browser_sync diff --git a/chrome/browser/sync/glue/typed_url_change_processor.h b/chrome/browser/sync/glue/typed_url_change_processor.h index 920c62e..7fb9eb6 100644 --- a/chrome/browser/sync/glue/typed_url_change_processor.h +++ b/chrome/browser/sync/glue/typed_url_change_processor.h @@ -24,6 +24,7 @@ namespace history { class HistoryBackend; struct URLsDeletedDetails; struct URLsModifiedDetails; +struct URLVisitedDetails; class URLRow; }; @@ -60,16 +61,12 @@ class TypedUrlChangeProcessor : public ChangeProcessor, virtual void StopImpl(); private: - bool WriteTypedUrl(sync_api::WriteNode* node, - const string16& name, - const string16& value, - std::vector<base::Time>& timestamps); - void StartObserving(); void StopObserving(); void HandleURLsModified(history::URLsModifiedDetails* details); void HandleURLsDeleted(history::URLsDeletedDetails* details); + void HandleURLsVisited(history::URLVisitedDetails* details); // The two models should be associated according to this ModelAssociator. TypedUrlModelAssociator* model_associator_; diff --git a/chrome/browser/sync/glue/typed_url_model_associator.cc b/chrome/browser/sync/glue/typed_url_model_associator.cc index 458ad79..b5f60f1 100644 --- a/chrome/browser/sync/glue/typed_url_model_associator.cc +++ b/chrome/browser/sync/glue/typed_url_model_associator.cc @@ -42,8 +42,21 @@ bool TypedUrlModelAssociator::AssociateModels() { return false; } + // Get all the visits. + std::map<history::URLID, history::VisitVector> visit_vectors; + for (std::vector<history::URLRow>::iterator ix = typed_urls.begin(); + ix != typed_urls.end(); ++ix) { + if (!history_backend_->GetVisitsForURL(ix->id(), + &(visit_vectors[ix->id()]))) { + LOG(ERROR) << "Could not get the url's visits."; + return false; + } + DCHECK(!visit_vectors[ix->id()].empty()); + } + TypedUrlTitleVector titles; TypedUrlVector new_urls; + TypedUrlVisitVector new_visits; TypedUrlUpdateVector updated_urls; { @@ -61,6 +74,13 @@ bool TypedUrlModelAssociator::AssociateModels() { ix != typed_urls.end(); ++ix) { std::string tag = ix->url().spec(); + history::VisitVector& visits = visit_vectors[ix->id()]; + DCHECK(visits.size() == static_cast<size_t>(ix->visit_count())); + if (visits.size() != static_cast<size_t>(ix->visit_count())) { + LOG(ERROR) << "Visit count does not match."; + return false; + } + sync_api::ReadNode node(&trans); if (node.InitByClientTagLookup(syncable::TYPED_URLS, tag)) { const sync_pb::TypedUrlSpecifics& typed_url( @@ -69,14 +89,16 @@ bool TypedUrlModelAssociator::AssociateModels() { history::URLRow new_url(ix->url()); - int difference = MergeUrls(typed_url, *ix, &new_url); + std::vector<base::Time> added_visits; + int difference = MergeUrls(typed_url, *ix, &visits, &new_url, + &added_visits); if (difference & DIFF_NODE_CHANGED) { sync_api::WriteNode write_node(&trans); if (!write_node.InitByClientTagLookup(syncable::TYPED_URLS, tag)) { LOG(ERROR) << "Failed to edit typed_url sync node."; return false; } - WriteToSyncNode(new_url, &write_node); + WriteToSyncNode(new_url, visits, &write_node); } if (difference & DIFF_TITLE_CHANGED) { titles.push_back(std::pair<GURL, std::wstring>(new_url.url(), @@ -86,6 +108,11 @@ bool TypedUrlModelAssociator::AssociateModels() { updated_urls.push_back( std::pair<history::URLID, history::URLRow>(ix->id(), new_url)); } + if (difference & DIFF_VISITS_ADDED) { + new_visits.push_back( + std::pair<GURL, std::vector<base::Time> >(ix->url(), + added_visits)); + } Associate(&tag, node.GetId()); } else { @@ -97,7 +124,7 @@ bool TypedUrlModelAssociator::AssociateModels() { } node.SetTitle(UTF8ToWide(tag)); - WriteToSyncNode(*ix, &node); + WriteToSyncNode(*ix, visits, &node); Associate(&tag, node.GetId()); } @@ -116,15 +143,31 @@ bool TypedUrlModelAssociator::AssociateModels() { sync_child_node.GetTypedUrlSpecifics()); if (current_urls.find(typed_url.url()) == current_urls.end()) { + new_visits.push_back( + std::pair<GURL, std::vector<base::Time> >( + GURL(typed_url.url()), + std::vector<base::Time>())); + std::vector<base::Time>& visits = new_visits.back().second; history::URLRow new_url(GURL(typed_url.url())); new_url.set_title(UTF8ToWide(typed_url.title())); - new_url.set_visit_count(typed_url.visit_count()); + + // When we add a new url, the last visit is always added, thus we set + // the initial visit count to one. This value will be automatically + // incremented as visits are added. + new_url.set_visit_count(1); new_url.set_typed_count(typed_url.typed_count()); - new_url.set_last_visit( - base::Time::FromInternalValue(typed_url.last_visit())); new_url.set_hidden(typed_url.hidden()); + // The latest visit gets added automatically, so skip it. + for (int c = 0; c < typed_url.visit_size() - 1; ++c) { + DCHECK(typed_url.visit(c) < typed_url.visit(c + 1)); + visits.push_back(base::Time::FromInternalValue(typed_url.visit(c))); + } + + new_url.set_last_visit(base::Time::FromInternalValue( + typed_url.visit(typed_url.visit_size() - 1))); + Associate(&typed_url.url(), sync_child_node.GetId()); new_urls.push_back(new_url); } @@ -138,9 +181,8 @@ bool TypedUrlModelAssociator::AssociateModels() { // this is the only thread that writes to the database. We also don't have // to worry about the sync model getting out of sync, because changes are // propogated to the ChangeProcessor on this thread. - WriteToHistoryBackend(&titles, &new_urls, &updated_urls); - - return true; + return WriteToHistoryBackend(&titles, &new_urls, &updated_urls, + &new_visits, NULL); } bool TypedUrlModelAssociator::DeleteAllNodes( @@ -235,10 +277,12 @@ bool TypedUrlModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, return true; } -void TypedUrlModelAssociator::WriteToHistoryBackend( +bool TypedUrlModelAssociator::WriteToHistoryBackend( const TypedUrlTitleVector* titles, const TypedUrlVector* new_urls, - const TypedUrlUpdateVector* updated_urls) { + const TypedUrlUpdateVector* updated_urls, + const TypedUrlVisitVector* new_visits, + const history::VisitVector* deleted_visits) { if (titles) { for (TypedUrlTitleVector::const_iterator title = titles->begin(); title != titles->end(); ++title) { @@ -251,24 +295,49 @@ void TypedUrlModelAssociator::WriteToHistoryBackend( if (updated_urls) { for (TypedUrlUpdateVector::const_iterator url = updated_urls->begin(); url != updated_urls->end(); ++url) { - history_backend_->UpdateURL(url->first, url->second); + if (!history_backend_->UpdateURL(url->first, url->second)) { + LOG(ERROR) << "Could not update page: " << url->second.url().spec(); + return false; + } + } + } + if (new_visits) { + for (TypedUrlVisitVector::const_iterator visits = new_visits->begin(); + visits != new_visits->end(); ++visits) { + if (!history_backend_->AddVisits(visits->first, visits->second)) { + LOG(ERROR) << "Could not add visits."; + return false; + } + } + } + if (deleted_visits) { + if (!history_backend_->RemoveVisits(*deleted_visits)) { + LOG(ERROR) << "Could not remove visits."; + return false; } } + return true; } // static int TypedUrlModelAssociator::MergeUrls( const sync_pb::TypedUrlSpecifics& typed_url, const history::URLRow& url, - history::URLRow* new_url) { + history::VisitVector* visits, + history::URLRow* new_url, + std::vector<base::Time>* new_visits) { DCHECK(new_url); DCHECK(!typed_url.url().compare(url.url().spec())); DCHECK(!typed_url.url().compare(new_url->url().spec())); + DCHECK(visits->size()); + + new_url->set_visit_count(visits->size()); // Convert these values only once. std::wstring typed_title(UTF8ToWide(typed_url.title())); base::Time typed_visit = - base::Time::FromInternalValue(typed_url.last_visit()); + base::Time::FromInternalValue( + typed_url.visit(typed_url.visit_size() - 1)); // This is a bitfield represting what we'll need to update with the output // value. @@ -276,12 +345,10 @@ int TypedUrlModelAssociator::MergeUrls( // Check if the non-incremented values changed. if ((typed_title.compare(url.title()) != 0) || - (typed_visit != url.last_visit()) || (typed_url.hidden() != url.hidden())) { // Use the values from the most recent visit. if (typed_visit >= url.last_visit()) { new_url->set_title(typed_title); - new_url->set_last_visit(typed_visit); new_url->set_hidden(typed_url.hidden()); different |= DIFF_ROW_CHANGED; @@ -291,55 +358,129 @@ int TypedUrlModelAssociator::MergeUrls( } } else { new_url->set_title(url.title()); - new_url->set_last_visit(url.last_visit()); new_url->set_hidden(url.hidden()); different |= DIFF_NODE_CHANGED; } } else { // No difference. new_url->set_title(url.title()); - new_url->set_last_visit(url.last_visit()); new_url->set_hidden(url.hidden()); } - // For visit count, we just select the maximum value. - if (typed_url.visit_count() > url.visit_count()) { - new_url->set_visit_count(typed_url.visit_count()); - different |= DIFF_ROW_CHANGED; - } else if (typed_url.visit_count() < url.visit_count()) { - new_url->set_visit_count(url.visit_count()); - different |= DIFF_NODE_CHANGED; - } else { - new_url->set_visit_count(typed_url.visit_count()); - } - // For typed count, we just select the maximum value. if (typed_url.typed_count() > url.typed_count()) { new_url->set_typed_count(typed_url.typed_count()); different |= DIFF_ROW_CHANGED; } else if (typed_url.typed_count() < url.typed_count()) { - new_url->set_typed_count(url.visit_count()); + new_url->set_typed_count(url.typed_count()); different |= DIFF_NODE_CHANGED; } else { // No difference. new_url->set_typed_count(typed_url.typed_count()); } + size_t left_visit_count = typed_url.visit_size(); + size_t right_visit_count = visits->size(); + size_t left = 0; + size_t right = 0; + while (left < left_visit_count && right < right_visit_count) { + base::Time left_time = base::Time::FromInternalValue(typed_url.visit(left)); + if (left_time < (*visits)[right].visit_time) { + different |= DIFF_VISITS_ADDED; + new_visits->push_back(left_time); + // This visit is added to visits below. + ++left; + } else if (left_time > (*visits)[right].visit_time) { + different |= DIFF_NODE_CHANGED; + ++right; + } else { + ++left; + ++right; + } + } + + for ( ; left < left_visit_count; ++left) { + different |= DIFF_VISITS_ADDED; + base::Time left_time = base::Time::FromInternalValue(typed_url.visit(left)); + new_visits->push_back(left_time); + // This visit is added to visits below. + } + if (different & DIFF_VISITS_ADDED) { + history::VisitVector::iterator visit_ix = visits->begin(); + for (std::vector<base::Time>::iterator new_visit = new_visits->begin(); + new_visit != new_visits->end(); ++new_visit) { + while (visit_ix != visits->end() && *new_visit > visit_ix->visit_time) { + ++visit_ix; + } + visit_ix = visits->insert(visit_ix, + history::VisitRow(url.id(), *new_visit, + 0, 0, 0)); + ++visit_ix; + } + } + + new_url->set_last_visit(visits->back().visit_time); + + DCHECK(static_cast<size_t>(new_url->visit_count()) == + (visits->size() - new_visits->size())); + return different; } // static -void TypedUrlModelAssociator::WriteToSyncNode(const history::URLRow& url, - sync_api::WriteNode* node) { +void TypedUrlModelAssociator::WriteToSyncNode( + const history::URLRow& url, + const history::VisitVector& visits, + sync_api::WriteNode* node) { + DCHECK(!url.last_visit().is_null()); + DCHECK(!visits.empty()); + DCHECK(url.last_visit() == visits.back().visit_time); + sync_pb::TypedUrlSpecifics typed_url; typed_url.set_url(url.url().spec()); typed_url.set_title(WideToUTF8(url.title())); - typed_url.set_visit_count(url.visit_count()); typed_url.set_typed_count(url.typed_count()); - typed_url.set_last_visit(url.last_visit().ToInternalValue()); typed_url.set_hidden(url.hidden()); + for (history::VisitVector::const_iterator visit = visits.begin(); + visit != visits.end(); ++visit) { + typed_url.add_visit(visit->visit_time.ToInternalValue()); + } + node->SetTypedUrlSpecifics(typed_url); } +// static +void TypedUrlModelAssociator::DiffVisits( + const history::VisitVector& old_visits, + const sync_pb::TypedUrlSpecifics& new_url, + std::vector<base::Time>* new_visits, + history::VisitVector* removed_visits) { + size_t left_visit_count = old_visits.size(); + size_t right_visit_count = new_url.visit_size(); + size_t left = 0; + size_t right = 0; + while (left < left_visit_count && right < right_visit_count) { + base::Time right_time = base::Time::FromInternalValue(new_url.visit(right)); + if (old_visits[left].visit_time < right_time) { + removed_visits->push_back(old_visits[left]); + ++left; + } else if (old_visits[left].visit_time > right_time) { + new_visits->push_back(right_time); + ++right; + } else { + ++left; + ++right; + } + } + + for ( ; left < left_visit_count; ++left) { + removed_visits->push_back(old_visits[left]); + } + + for ( ; right < right_visit_count; ++right) { + new_visits->push_back(base::Time::FromInternalValue(new_url.visit(right))); + } +} + } // namespace browser_sync diff --git a/chrome/browser/sync/glue/typed_url_model_associator.h b/chrome/browser/sync/glue/typed_url_model_associator.h index 82f4fa6..5aea994 100644 --- a/chrome/browser/sync/glue/typed_url_model_associator.h +++ b/chrome/browser/sync/glue/typed_url_model_associator.h @@ -50,7 +50,9 @@ class TypedUrlModelAssociator typedef std::vector<std::pair<GURL, std::wstring> > TypedUrlTitleVector; typedef std::vector<history::URLRow> TypedUrlVector; typedef std::vector<std::pair<history::URLID, history::URLRow> > - TypedUrlUpdateVector; + TypedUrlUpdateVector; + typedef std::vector<std::pair<GURL, std::vector<base::Time> > > + TypedUrlVisitVector; static syncable::ModelType model_type() { return syncable::TYPED_URLS; } TypedUrlModelAssociator(ProfileSyncService* sync_service, @@ -106,23 +108,34 @@ class TypedUrlModelAssociator // |sync_id| with that node's id. virtual bool GetSyncIdForTaggedNode(const std::string& tag, int64* sync_id); - void WriteToHistoryBackend(const TypedUrlTitleVector* titles, + bool WriteToHistoryBackend(const TypedUrlTitleVector* titles, const TypedUrlVector* new_urls, - const TypedUrlUpdateVector* updated_urls); + const TypedUrlUpdateVector* updated_urls, + const TypedUrlVisitVector* new_visits, + const history::VisitVector* deleted_visits); enum { DIFF_NONE = 0x0000, DIFF_NODE_CHANGED = 0x0001, DIFF_TITLE_CHANGED = 0x0002, DIFF_ROW_CHANGED = 0x0004, + DIFF_VISITS_ADDED = 0x0008, }; static int MergeUrls(const sync_pb::TypedUrlSpecifics& typed_url, const history::URLRow& url, - history::URLRow* new_url); + history::VisitVector* visits, + history::URLRow* new_url, + std::vector<base::Time>* new_visits); static void WriteToSyncNode(const history::URLRow& url, + const history::VisitVector& visits, sync_api::WriteNode* node); + static void DiffVisits(const history::VisitVector& old_visits, + const sync_pb::TypedUrlSpecifics& new_url, + std::vector<base::Time>* new_visits, + history::VisitVector* removed_visits); + protected: // Returns sync service instance. ProfileSyncService* sync_service() { return sync_service_; } diff --git a/chrome/browser/sync/glue/typed_url_model_associator_unittest.cc b/chrome/browser/sync/glue/typed_url_model_associator_unittest.cc index 397e4e2..238b575 100644 --- a/chrome/browser/sync/glue/typed_url_model_associator_unittest.cc +++ b/chrome/browser/sync/glue/typed_url_model_associator_unittest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/basictypes.h" #include "base/string_piece.h" #include "base/utf_string_conversions.h" #include "chrome/browser/history/history_types.h" @@ -16,34 +17,34 @@ class TypedUrlModelAssociatorTest : public testing::Test { public: static history::URLRow MakeTypedUrlRow(const char* url, const char* title, - int visit_count, int typed_count, int64 last_visit, - bool hidden) { + bool hidden, + history::VisitVector* visits) { GURL gurl(url); history::URLRow history_url(gurl); history_url.set_title(UTF8ToWide(title)); - history_url.set_visit_count(visit_count); history_url.set_typed_count(typed_count); history_url.set_last_visit( base::Time::FromInternalValue(last_visit)); history_url.set_hidden(hidden); + visits->push_back(history::VisitRow( + history_url.id(), history_url.last_visit(), 0, 0, 0)); + history_url.set_visit_count(visits->size()); return history_url; } static sync_pb::TypedUrlSpecifics MakeTypedUrlSpecifics(const char* url, const char* title, - int visit_count, int typed_count, int64 last_visit, bool hidden) { sync_pb::TypedUrlSpecifics typed_url; typed_url.set_url(url); typed_url.set_title(title); - typed_url.set_visit_count(visit_count); typed_url.set_typed_count(typed_count); - typed_url.set_last_visit(last_visit); typed_url.set_hidden(hidden); + typed_url.add_visit(last_visit); return typed_url; } @@ -52,69 +53,173 @@ class TypedUrlModelAssociatorTest : public testing::Test { (lhs.title().compare(rhs.title()) == 0) && (lhs.visit_count() == rhs.visit_count()) && (lhs.typed_count() == rhs.typed_count()) && - (lhs.last_visit() == rhs.last_visit()) && (lhs.hidden() == rhs.hidden()); } }; TEST_F(TypedUrlModelAssociatorTest, MergeUrls) { + history::VisitVector visits1; history::URLRow row1(MakeTypedUrlRow("http://pie.com/", "pie", - 1, 2, 3, false)); + 2, 3, false, &visits1)); sync_pb::TypedUrlSpecifics specs1(MakeTypedUrlSpecifics("http://pie.com/", "pie", - 1, 2, 3, false)); + 2, 3, false)); history::URLRow new_row1(GURL("http://pie.com/")); + std::vector<base::Time> new_visits1; EXPECT_EQ(TypedUrlModelAssociator::DIFF_NONE, - TypedUrlModelAssociator::MergeUrls(specs1, row1, &new_row1)); + TypedUrlModelAssociator::MergeUrls(specs1, row1, &visits1, + &new_row1, &new_visits1)); + history::VisitVector visits2; history::URLRow row2(MakeTypedUrlRow("http://pie.com/", "pie", - 1, 2, 3, false)); + 2, 3, false, &visits2)); sync_pb::TypedUrlSpecifics specs2(MakeTypedUrlSpecifics("http://pie.com/", "pie", - 1, 2, 4, true)); + 2, 3, true)); + history::VisitVector expected_visits2; history::URLRow expected2(MakeTypedUrlRow("http://pie.com/", "pie", - 1, 2, 4, true)); + 2, 3, true, &expected_visits2)); history::URLRow new_row2(GURL("http://pie.com/")); + std::vector<base::Time> new_visits2; EXPECT_EQ(TypedUrlModelAssociator::DIFF_ROW_CHANGED, - TypedUrlModelAssociator::MergeUrls(specs2, row2, &new_row2)); + TypedUrlModelAssociator::MergeUrls(specs2, row2, &visits2, + &new_row2, &new_visits2)); EXPECT_TRUE(URLsEqual(new_row2, expected2)); + history::VisitVector visits3; history::URLRow row3(MakeTypedUrlRow("http://pie.com/", "pie", - 1, 2, 3, false)); + 2, 3, false, &visits3)); sync_pb::TypedUrlSpecifics specs3(MakeTypedUrlSpecifics("http://pie.com/", "pie2", - 1, 2, 4, true)); + 2, 3, true)); + history::VisitVector expected_visits3; history::URLRow expected3(MakeTypedUrlRow("http://pie.com/", "pie2", - 1, 2, 4, true)); + 2, 3, true, &expected_visits3)); history::URLRow new_row3(GURL("http://pie.com/")); + std::vector<base::Time> new_visits3; EXPECT_EQ(TypedUrlModelAssociator::DIFF_ROW_CHANGED | TypedUrlModelAssociator::DIFF_TITLE_CHANGED, - TypedUrlModelAssociator::MergeUrls(specs3, row3, &new_row3)); + TypedUrlModelAssociator::MergeUrls(specs3, row3, &visits3, + &new_row3, &new_visits3)); EXPECT_TRUE(URLsEqual(new_row3, expected3)); + history::VisitVector visits4; history::URLRow row4(MakeTypedUrlRow("http://pie.com/", "pie", - 1, 2, 4, false)); + 2, 4, false, &visits4)); sync_pb::TypedUrlSpecifics specs4(MakeTypedUrlSpecifics("http://pie.com/", "pie2", - 1, 2, 3, true)); + 2, 3, true)); + history::VisitVector expected_visits4; history::URLRow expected4(MakeTypedUrlRow("http://pie.com/", "pie", - 1, 2, 4, false)); + 2, 3, false, &expected_visits4)); history::URLRow new_row4(GURL("http://pie.com/")); - EXPECT_EQ(TypedUrlModelAssociator::DIFF_NODE_CHANGED, - TypedUrlModelAssociator::MergeUrls(specs4, row4, &new_row4)); + std::vector<base::Time> new_visits4; + EXPECT_EQ(TypedUrlModelAssociator::DIFF_NODE_CHANGED | + TypedUrlModelAssociator::DIFF_VISITS_ADDED, + TypedUrlModelAssociator::MergeUrls(specs4, row4, &visits4, + &new_row4, &new_visits4)); EXPECT_TRUE(URLsEqual(new_row4, expected4)); + history::VisitVector visits5; history::URLRow row5(MakeTypedUrlRow("http://pie.com/", "pie", - 2, 1, 3, false)); + 1, 4, false, &visits5)); sync_pb::TypedUrlSpecifics specs5(MakeTypedUrlSpecifics("http://pie.com/", "pie", - 1, 2, 3, false)); + 2, 3, false)); + history::VisitVector expected_visits5; history::URLRow expected5(MakeTypedUrlRow("http://pie.com/", "pie", - 2, 2, 3, false)); + 2, 3, false, &expected_visits5)); history::URLRow new_row5(GURL("http://pie.com/")); + std::vector<base::Time> new_visits5; EXPECT_EQ(TypedUrlModelAssociator::DIFF_ROW_CHANGED | - TypedUrlModelAssociator::DIFF_NODE_CHANGED, - TypedUrlModelAssociator::MergeUrls(specs5, row5, &new_row5)); + TypedUrlModelAssociator::DIFF_VISITS_ADDED, + TypedUrlModelAssociator::MergeUrls(specs5, row5, &visits5, + &new_row5, &new_visits5)); EXPECT_TRUE(URLsEqual(new_row5, expected5)); } + +TEST_F(TypedUrlModelAssociatorTest, DiffVisitsSame) { + history::VisitVector old_visits; + sync_pb::TypedUrlSpecifics new_url; + + const int64 visits[] = { 1024, 2065, 65534, 1237684 }; + + for (size_t c = 0; c < arraysize(visits); ++c) { + old_visits.push_back(history::VisitRow( + 0, base::Time::FromInternalValue(visits[c]), 0, 0, 0)); + new_url.add_visit(visits[c]); + } + + std::vector<base::Time> new_visits; + history::VisitVector removed_visits; + + TypedUrlModelAssociator::DiffVisits(old_visits, new_url, + &new_visits, &removed_visits); + EXPECT_TRUE(new_visits.empty()); + EXPECT_TRUE(removed_visits.empty()); +} + +TEST_F(TypedUrlModelAssociatorTest, DiffVisitsRemove) { + history::VisitVector old_visits; + sync_pb::TypedUrlSpecifics new_url; + + const int64 visits_left[] = { 1, 1024, 1500, 2065, 6000, + 65534, 1237684, 2237684 }; + const int64 visits_right[] = { 1024, 2065, 65534, 1237684 }; + + const int64 visits_removed[] = { 1, 1500, 6000, 2237684 }; + + for (size_t c = 0; c < arraysize(visits_left); ++c) { + old_visits.push_back(history::VisitRow( + 0, base::Time::FromInternalValue(visits_left[c]), 0, 0, 0)); + } + + for (size_t c = 0; c < arraysize(visits_right); ++c) { + new_url.add_visit(visits_right[c]); + } + + std::vector<base::Time> new_visits; + history::VisitVector removed_visits; + + TypedUrlModelAssociator::DiffVisits(old_visits, new_url, + &new_visits, &removed_visits); + EXPECT_TRUE(new_visits.empty()); + ASSERT_TRUE(removed_visits.size() == arraysize(visits_removed)); + for (size_t c = 0; c < arraysize(visits_removed); ++c) { + EXPECT_EQ(removed_visits[c].visit_time.ToInternalValue(), + visits_removed[c]); + } +} + +TEST_F(TypedUrlModelAssociatorTest, DiffVisitsAdd) { + history::VisitVector old_visits; + sync_pb::TypedUrlSpecifics new_url; + + const int64 visits_left[] = { 1024, 2065, 65534, 1237684 }; + const int64 visits_right[] = { 1, 1024, 1500, 2065, 6000, + 65534, 1237684, 2237684 }; + + const int64 visits_added[] = { 1, 1500, 6000, 2237684 }; + + for (size_t c = 0; c < arraysize(visits_left); ++c) { + old_visits.push_back(history::VisitRow( + 0, base::Time::FromInternalValue(visits_left[c]), 0, 0, 0)); + } + + for (size_t c = 0; c < arraysize(visits_right); ++c) { + new_url.add_visit(visits_right[c]); + } + + std::vector<base::Time> new_visits; + history::VisitVector removed_visits; + + TypedUrlModelAssociator::DiffVisits(old_visits, new_url, + &new_visits, &removed_visits); + EXPECT_TRUE(removed_visits.empty()); + ASSERT_TRUE(new_visits.size() == arraysize(visits_added)); + for (size_t c = 0; c < arraysize(visits_added); ++c) { + EXPECT_EQ(new_visits[c].ToInternalValue(), + visits_added[c]); + } +} diff --git a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc index e784b49..13995db 100644 --- a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc @@ -74,9 +74,14 @@ class HistoryBackendMock : public HistoryBackend { public: HistoryBackendMock() : HistoryBackend(FilePath(), NULL, NULL) {} MOCK_METHOD1(GetAllTypedURLs, bool(std::vector<history::URLRow>* entries)); - MOCK_METHOD2(UpdateURL, bool(history::URLID id, history::URLRow& url)); - MOCK_METHOD2(SetPageTitle, void(const GURL& url, const std::wstring& title)); + MOCK_METHOD2(GetVisitsForURL, bool(history::URLID id, + history::VisitVector* visits)); + MOCK_METHOD2(UpdateURL, bool(history::URLID id, const history::URLRow& url)); + MOCK_METHOD2(AddVisits, bool(const GURL& url, + const std::vector<base::Time>& visits)); + MOCK_METHOD1(RemoveVisits, bool(const history::VisitVector& visits)); MOCK_METHOD2(GetURL, bool(const GURL& url_id, history::URLRow* url_row)); + MOCK_METHOD2(SetPageTitle, void(const GURL& url, const std::wstring& title)); MOCK_METHOD1(DeleteURL, void(const GURL& url)); }; @@ -204,7 +209,8 @@ class ProfileSyncServiceTypedUrlTest : public testing::Test { node.Put(SPECIFICS, specifics); } - void AddTypedUrlSyncNode(const history::URLRow& url) { + void AddTypedUrlSyncNode(const history::URLRow& url, + const history::VisitVector& visits) { sync_api::WriteTransaction trans( service_->backend()->GetUserShareHandle()); sync_api::ReadNode typed_url_root(&trans); @@ -215,7 +221,7 @@ class ProfileSyncServiceTypedUrlTest : public testing::Test { ASSERT_TRUE(node.InitUniqueByCreation(syncable::TYPED_URLS, typed_url_root, tag)); - TypedUrlModelAssociator::WriteToSyncNode(url, &node); + TypedUrlModelAssociator::WriteToSyncNode(url, visits, &node); } void GetTypedUrlsFromSyncDB(std::vector<history::URLRow>* urls) { @@ -235,10 +241,11 @@ class ProfileSyncServiceTypedUrlTest : public testing::Test { history::URLRow new_url(GURL(typed_url.url())); new_url.set_title(UTF8ToWide(typed_url.title())); - new_url.set_visit_count(typed_url.visit_count()); new_url.set_typed_count(typed_url.typed_count()); - new_url.set_last_visit( - base::Time::FromInternalValue(typed_url.last_visit())); + DCHECK(typed_url.visit_size()); + new_url.set_visit_count(typed_url.visit_size()); + new_url.set_last_visit(base::Time::FromInternalValue( + typed_url.visit(typed_url.visit_size() - 1))); new_url.set_hidden(typed_url.hidden()); urls->push_back(new_url); @@ -264,18 +271,20 @@ class ProfileSyncServiceTypedUrlTest : public testing::Test { static history::URLRow MakeTypedUrlEntry(const char* url, const char* title, - int visit_count, int typed_count, int64 last_visit, - bool hidden) { + bool hidden, + history::VisitVector* visits) { GURL gurl(url); URLRow history_url(gurl); history_url.set_title(UTF8ToWide(title)); - history_url.set_visit_count(visit_count); history_url.set_typed_count(typed_count); history_url.set_last_visit( base::Time::FromInternalValue(last_visit)); history_url.set_hidden(hidden); + visits->push_back(history::VisitRow( + history_url.id(), history_url.last_visit(), 0, 0, 0)); + history_url.set_visit_count(visits->size()); return history_url; } @@ -322,7 +331,10 @@ class AddTypedUrlEntriesTask : public Task { virtual void Run() { test_->CreateTypedUrlRoot(); for (size_t i = 0; i < entries_.size(); ++i) { - test_->AddTypedUrlSyncNode(entries_[i]); + history::VisitVector visits; + visits.push_back(history::VisitRow( + entries_[i].id(), entries_[i].last_visit(), 0, 0, 0)); + test_->AddTypedUrlSyncNode(entries_[i], visits); } } @@ -344,10 +356,14 @@ TEST_F(ProfileSyncServiceTypedUrlTest, EmptyNativeEmptySync) { TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeEmptySync) { std::vector<history::URLRow> entries; + history::VisitVector visits; entries.push_back(MakeTypedUrlEntry("http://foo.com", "bar", - 1, 2, 15, false)); + 2, 15, false, &visits)); + EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); + EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)). + WillRepeatedly(DoAll(SetArgumentPointee<1>(visits), Return(true))); SetIdleChangeProcessorExpectations(); CreateTypedUrlRootTask task(this); StartSyncService(&task); @@ -358,15 +374,21 @@ TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeEmptySync) { } TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncNoMerge) { + history::VisitVector native_visits; + history::VisitVector sync_visits; history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", - 1, 2, 15, false)); + 2, 15, false, &native_visits)); history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry", - 2, 3, 16, false)); + 3, 16, false, &sync_visits)); std::vector<history::URLRow> native_entries; native_entries.push_back(native_entry); EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); + EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)). + WillRepeatedly(DoAll(SetArgumentPointee<1>(native_visits), Return(true))); + EXPECT_CALL((*history_backend_.get()), AddVisits(_, _)). + WillRepeatedly(Return(true)); std::vector<history::URLRow> sync_entries; sync_entries.push_back(sync_entry); @@ -391,17 +413,27 @@ TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncNoMerge) { } TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncMerge) { + history::VisitVector native_visits; history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", - 1, 2, 15, false)); + 2, 15, false, &native_visits)); + history::VisitVector sync_visits; history::URLRow sync_entry(MakeTypedUrlEntry("http://native.com", "name", - 2, 1, 17, false)); + 1, 17, false, &sync_visits)); + history::VisitVector merged_visits; + merged_visits.push_back(history::VisitRow( + sync_entry.id(), base::Time::FromInternalValue(15), 0, 0, 0)); + history::URLRow merged_entry(MakeTypedUrlEntry("http://native.com", "name", - 2, 1, 17, false)); + 2, 17, false, &merged_visits)); std::vector<history::URLRow> native_entries; native_entries.push_back(native_entry); EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); + EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)). + WillRepeatedly(DoAll(SetArgumentPointee<1>(native_visits), Return(true))); + EXPECT_CALL((*history_backend_.get()), AddVisits(_, _)). + WillRepeatedly(Return(true)); std::vector<history::URLRow> sync_entries; sync_entries.push_back(sync_entry); @@ -420,15 +452,19 @@ TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncMerge) { } TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAdd) { + history::VisitVector added_visits; + history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry", + 2, 15, false, &added_visits)); + EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). WillOnce(Return(true)); + EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)). + WillOnce(DoAll(SetArgumentPointee<1>(added_visits), Return(true))); + SetIdleChangeProcessorExpectations(); CreateTypedUrlRootTask task(this); StartSyncService(&task); - history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry", - 1, 2, 15, false)); - history::URLsModifiedDetails details; details.changed_urls.push_back(added_entry); scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&history_thread_); @@ -442,18 +478,25 @@ TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAdd) { } TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeUpdate) { + history::VisitVector original_visits; history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry", - 1, 2, 15, false)); + 2, 15, false, + &original_visits)); std::vector<history::URLRow> original_entries; original_entries.push_back(original_entry); EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); + EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)). + WillRepeatedly(DoAll(SetArgumentPointee<1>(original_visits), + Return(true))); CreateTypedUrlRootTask task(this); StartSyncService(&task); + history::VisitVector updated_visits; history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry", - 3, 7, 19, false)); + 7, 15, false, + &updated_visits)); history::URLsModifiedDetails details; details.changed_urls.push_back(updated_entry); @@ -468,17 +511,24 @@ TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeUpdate) { } TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemove) { + history::VisitVector original_visits1; history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry", - 1, 2, 15, false)); + 2, 15, false, + &original_visits1)); + history::VisitVector original_visits2; history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com", "entry2", - 2, 3, 17, false)); + 3, 15, false, + &original_visits2)); std::vector<history::URLRow> original_entries; original_entries.push_back(original_entry1); original_entries.push_back(original_entry2); EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); + EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)). + WillRepeatedly(DoAll(SetArgumentPointee<1>(original_visits1), + Return(true))); CreateTypedUrlRootTask task(this); StartSyncService(&task); @@ -496,17 +546,24 @@ TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemove) { } TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemoveAll) { + history::VisitVector original_visits1; history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry", - 1, 2, 15, false)); + 2, 15, false, + &original_visits1)); + history::VisitVector original_visits2; history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com", "entry2", - 2, 3, 17, false)); + 3, 15, false, + &original_visits2)); std::vector<history::URLRow> original_entries; original_entries.push_back(original_entry1); original_entries.push_back(original_entry2); EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); + EXPECT_CALL((*history_backend_.get()), GetVisitsForURL(_, _)). + WillRepeatedly(DoAll(SetArgumentPointee<1>(original_visits1), + Return(true))); CreateTypedUrlRootTask task(this); StartSyncService(&task); diff --git a/chrome/browser/sync/protocol/typed_url_specifics.proto b/chrome/browser/sync/protocol/typed_url_specifics.proto index a6bd309..f9112b6 100644 --- a/chrome/browser/sync/protocol/typed_url_specifics.proto +++ b/chrome/browser/sync/protocol/typed_url_specifics.proto @@ -16,10 +16,9 @@ import "sync.proto"; message TypedUrlSpecifics { optional string url = 1; optional string title = 2; - optional int32 visit_count = 3; - optional int32 typed_count = 4; - optional int64 last_visit = 5; - optional bool hidden = 6; + optional int32 typed_count = 3; + optional bool hidden = 4; + repeated int64 visit = 5; } extend EntitySpecifics { |