| Index: components/history/core/browser/typed_url_sync_bridge.cc
|
| diff --git a/components/history/core/browser/typed_url_sync_bridge.cc b/components/history/core/browser/typed_url_sync_bridge.cc
|
| index 150ae7c50581b5cc8074b7a278843c15b29785aa..9a437f71686aae11510644ee94d62f86e0caff74 100644
|
| --- a/components/history/core/browser/typed_url_sync_bridge.cc
|
| +++ b/components/history/core/browser/typed_url_sync_bridge.cc
|
| @@ -36,6 +36,14 @@ static const int kMaxTypedUrlVisits = 100;
|
| // RELOAD visits, which will be stripped.
|
| static const int kMaxVisitsToFetch = 1000;
|
|
|
| +// This is the threshold at which we start throttling sync updates for typed
|
| +// URLs - any URLs with a typed_count >= this threshold will be throttled.
|
| +static const int kTypedUrlVisitThrottleThreshold = 10;
|
| +
|
| +// This is the multiple we use when throttling sync updates. If the multiple is
|
| +// N, we sync up every Nth update (i.e. when typed_count % N == 0).
|
| +static const int kTypedUrlVisitThrottleMultiple = 10;
|
| +
|
| // Enforce oldest to newest visit order.
|
| static bool CheckVisitOrdering(const VisitVector& visits) {
|
| int64_t previous_visit_time = 0;
|
| @@ -51,14 +59,15 @@ static bool CheckVisitOrdering(const VisitVector& visits) {
|
| }
|
|
|
| std::string GetStorageKeyFromURLRow(const URLRow& row) {
|
| + DCHECK_NE(row.id(), 0);
|
| std::string storage_key(sizeof(row.id()), 0);
|
| base::WriteBigEndian<URLID>(&storage_key[0], row.id());
|
| return storage_key;
|
| }
|
|
|
| bool HasTypedUrl(const VisitVector& visits) {
|
| - auto typed_url_visit = std::find_if(
|
| - visits.begin(), visits.end(), [](const history::VisitRow& visit) {
|
| + auto typed_url_visit =
|
| + std::find_if(visits.begin(), visits.end(), [](const VisitRow& visit) {
|
| return ui::PageTransitionCoreTypeIs(visit.transition,
|
| ui::PAGE_TRANSITION_TYPED);
|
| });
|
| @@ -98,7 +107,7 @@ base::Optional<ModelError> TypedURLSyncBridge::MergeSyncData(
|
| DCHECK(sequence_checker_.CalledOnValidSequence());
|
|
|
| // Create a mapping of all local data by URLID. These will be narrowed down
|
| - // by CreateOrUpdateUrl() to include only the entries different from sync
|
| + // by MergeURLWithSync() to include only the entries different from sync
|
| // server data.
|
| TypedURLMap new_db_urls;
|
|
|
| @@ -111,12 +120,12 @@ base::Optional<ModelError> TypedURLSyncBridge::MergeSyncData(
|
| }
|
|
|
| // New sync data organized for different write operations to history backend.
|
| - history::URLRows new_synced_urls;
|
| - history::URLRows updated_synced_urls;
|
| + URLRows new_synced_urls;
|
| + URLRows updated_synced_urls;
|
| TypedURLVisitVector new_synced_visits;
|
|
|
| // Iterate through entity_data and check for all the urls that
|
| - // sync already knows about. CreateOrUpdateUrl() will remove urls that
|
| + // sync already knows about. MergeURLWithSync() will remove urls that
|
| // are the same as the synced ones from |new_db_urls|.
|
| for (const EntityChange& entity_change : entity_data) {
|
| DCHECK(entity_change.data().specifics.has_typed_url());
|
| @@ -137,20 +146,14 @@ base::Optional<ModelError> TypedURLSyncBridge::MergeSyncData(
|
|
|
| continue;
|
| }
|
| - UpdateUrlFromServer(specifics, &new_db_urls, &local_visit_vectors,
|
| - &new_synced_urls, &new_synced_visits,
|
| - &updated_synced_urls);
|
| + MergeURLWithSync(specifics, &new_db_urls, &local_visit_vectors,
|
| + &new_synced_urls, &new_synced_visits,
|
| + &updated_synced_urls);
|
| }
|
|
|
| for (const auto& kv : new_db_urls) {
|
| - if (!HasTypedUrl(local_visit_vectors[kv.first])) {
|
| - // This URL has no TYPED visits, don't sync it
|
| - continue;
|
| - }
|
| - std::string storage_key = GetStorageKeyFromURLRow(kv.second);
|
| - change_processor()->Put(
|
| - storage_key, CreateEntityData(kv.second, local_visit_vectors[kv.first]),
|
| - metadata_change_list.get());
|
| + SendTypedURLToProcessor(kv.second, local_visit_vectors[kv.first],
|
| + metadata_change_list.get());
|
| }
|
|
|
| base::Optional<ModelError> error = WriteToHistoryBackend(
|
| @@ -165,6 +168,7 @@ base::Optional<ModelError> TypedURLSyncBridge::MergeSyncData(
|
| GetStorageKeyInternal(entity_change.data().specifics.typed_url().url());
|
| if (storage_key.empty()) {
|
| // ignore entity change
|
| + change_processor()->UntrackEntity(entity_change.data());
|
| } else {
|
| change_processor()->UpdateStorageKey(entity_change.data(), storage_key,
|
| metadata_change_list.get());
|
| @@ -184,20 +188,106 @@ base::Optional<ModelError> TypedURLSyncBridge::ApplySyncChanges(
|
| std::unique_ptr<MetadataChangeList> metadata_change_list,
|
| EntityChangeList entity_changes) {
|
| DCHECK(sequence_checker_.CalledOnValidSequence());
|
| - NOTIMPLEMENTED();
|
| + DCHECK(sync_metadata_database_);
|
| +
|
| + std::vector<GURL> pending_deleted_urls;
|
| + TypedURLVisitVector new_synced_visits;
|
| + VisitVector deleted_visits;
|
| + URLRows updated_synced_urls;
|
| + URLRows new_synced_urls;
|
| +
|
| + for (const EntityChange& entity_change : entity_changes) {
|
| + if (entity_change.type() == EntityChange::ACTION_DELETE) {
|
| + URLRow url_row;
|
| + int64_t url_id = sync_metadata_database_->StorageKeyToURLID(
|
| + entity_change.storage_key());
|
| + if (!history_backend_->GetURLByID(url_id, &url_row)) {
|
| + // Ignoring the case that there is no matching URLRow with URLID
|
| + // |url_id|.
|
| + continue;
|
| + }
|
| +
|
| + pending_deleted_urls.push_back(url_row.url());
|
| + continue;
|
| + }
|
| +
|
| + DCHECK(entity_change.data().specifics.has_typed_url());
|
| + const TypedUrlSpecifics& specifics =
|
| + entity_change.data().specifics.typed_url();
|
| +
|
| + GURL url(specifics.url());
|
| +
|
| + if (ShouldIgnoreUrl(url))
|
| + continue;
|
| +
|
| + DCHECK(specifics.visits_size());
|
| + sync_pb::TypedUrlSpecifics filtered_url = FilterExpiredVisits(specifics);
|
| + if (filtered_url.visits_size() == 0)
|
| + continue;
|
| +
|
| + UpdateFromSync(filtered_url, &new_synced_visits, &deleted_visits,
|
| + &updated_synced_urls, &new_synced_urls);
|
| + }
|
| +
|
| + WriteToHistoryBackend(&new_synced_urls, &updated_synced_urls,
|
| + &pending_deleted_urls, &new_synced_visits,
|
| + &deleted_visits);
|
| +
|
| + // New entities were either ignored or written to history DB and assigned a
|
| + // storage key. Notify processor about updated storage keys.
|
| + for (const EntityChange& entity_change : entity_changes) {
|
| + if (entity_change.type() == EntityChange::ACTION_ADD) {
|
| + std::string storage_key = GetStorageKeyInternal(
|
| + entity_change.data().specifics.typed_url().url());
|
| + if (storage_key.empty()) {
|
| + // ignore entity change
|
| + change_processor()->UntrackEntity(entity_change.data());
|
| + } else {
|
| + change_processor()->UpdateStorageKey(entity_change.data(), storage_key,
|
| + metadata_change_list.get());
|
| + }
|
| + }
|
| + }
|
| +
|
| return {};
|
| }
|
|
|
| void TypedURLSyncBridge::GetData(StorageKeyList storage_keys,
|
| DataCallback callback) {
|
| DCHECK(sequence_checker_.CalledOnValidSequence());
|
| - NOTIMPLEMENTED();
|
| + DCHECK(sync_metadata_database_);
|
| +
|
| + auto batch = base::MakeUnique<MutableDataBatch>();
|
| + for (const std::string& key : storage_keys) {
|
| + URLRow url_row;
|
| + URLID url_id = sync_metadata_database_->StorageKeyToURLID(key);
|
| +
|
| + ++num_db_accesses_;
|
| + if (!history_backend_->GetURLByID(url_id, &url_row)) {
|
| + // Ignoring the case which no matching URLRow with URLID |url_id|.
|
| + DLOG(ERROR) << "Could not find URL for id: " << url_id;
|
| + continue;
|
| + }
|
| +
|
| + VisitVector visits_vector;
|
| + FixupURLAndGetVisits(&url_row, &visits_vector);
|
| + std::unique_ptr<syncer::EntityData> entity_data =
|
| + CreateEntityData(url_row, visits_vector);
|
| + if (!entity_data.get()) {
|
| + // Cannot create EntityData, ex. no TYPED visits.
|
| + continue;
|
| + }
|
| +
|
| + batch->Put(key, std::move(entity_data));
|
| + }
|
| +
|
| + callback.Run(std::move(batch));
|
| }
|
|
|
| void TypedURLSyncBridge::GetAllData(DataCallback callback) {
|
| DCHECK(sequence_checker_.CalledOnValidSequence());
|
|
|
| - history::URLRows typed_urls;
|
| + URLRows typed_urls;
|
| ++num_db_accesses_;
|
| if (!history_backend_->GetAllTypedURLs(&typed_urls)) {
|
| ++num_db_errors_;
|
| @@ -207,12 +297,19 @@ void TypedURLSyncBridge::GetAllData(DataCallback callback) {
|
| }
|
|
|
| auto batch = base::MakeUnique<MutableDataBatch>();
|
| - for (history::URLRow& url : typed_urls) {
|
| + for (URLRow& url : typed_urls) {
|
| VisitVector visits_vector;
|
| FixupURLAndGetVisits(&url, &visits_vector);
|
| - batch->Put(GetStorageKeyFromURLRow(url),
|
| - CreateEntityData(url, visits_vector));
|
| + std::unique_ptr<syncer::EntityData> entity_data =
|
| + CreateEntityData(url, visits_vector);
|
| + if (!entity_data.get()) {
|
| + // Cannot create EntityData, ex. no TYPED visits.
|
| + continue;
|
| + }
|
| +
|
| + batch->Put(GetStorageKeyFromURLRow(url), std::move(entity_data));
|
| }
|
| +
|
| callback.Run(std::move(batch));
|
| }
|
|
|
| @@ -239,29 +336,84 @@ bool TypedURLSyncBridge::SupportsGetStorageKey() const {
|
| return false;
|
| }
|
|
|
| -void TypedURLSyncBridge::OnURLVisited(history::HistoryBackend* history_backend,
|
| +void TypedURLSyncBridge::OnURLVisited(HistoryBackend* history_backend,
|
| ui::PageTransition transition,
|
| - const history::URLRow& row,
|
| - const history::RedirectList& redirects,
|
| + const URLRow& row,
|
| + const RedirectList& redirects,
|
| base::Time visit_time) {
|
| DCHECK(sequence_checker_.CalledOnValidSequence());
|
| - NOTIMPLEMENTED();
|
| +
|
| + if (!change_processor()->IsTrackingMetadata())
|
| + return; // Sync processor not yet ready, don't sync.
|
| + if (!ShouldSyncVisit(row.typed_count(), transition))
|
| + return;
|
| +
|
| + std::unique_ptr<MetadataChangeList> metadata_change_list =
|
| + CreateMetadataChangeList();
|
| +
|
| + UpdateSyncFromLocal(row, metadata_change_list.get());
|
| }
|
|
|
| -void TypedURLSyncBridge::OnURLsModified(
|
| - history::HistoryBackend* history_backend,
|
| - const history::URLRows& changed_urls) {
|
| +void TypedURLSyncBridge::OnURLsModified(HistoryBackend* history_backend,
|
| + const URLRows& changed_urls) {
|
| DCHECK(sequence_checker_.CalledOnValidSequence());
|
| - NOTIMPLEMENTED();
|
| +
|
| + if (!change_processor()->IsTrackingMetadata())
|
| + return; // Sync processor not yet ready, don't sync.
|
| +
|
| + std::unique_ptr<MetadataChangeList> metadata_change_list =
|
| + CreateMetadataChangeList();
|
| +
|
| + for (const auto& row : changed_urls) {
|
| + // Only care if the modified URL is typed.
|
| + if (row.typed_count() >= 0) {
|
| + // If there were any errors updating the sync node, just ignore them and
|
| + // continue on to process the next URL.
|
| + UpdateSyncFromLocal(row, metadata_change_list.get());
|
| + }
|
| + }
|
| }
|
|
|
| -void TypedURLSyncBridge::OnURLsDeleted(history::HistoryBackend* history_backend,
|
| +void TypedURLSyncBridge::OnURLsDeleted(HistoryBackend* history_backend,
|
| bool all_history,
|
| bool expired,
|
| - const history::URLRows& deleted_rows,
|
| + const URLRows& deleted_rows,
|
| const std::set<GURL>& favicon_urls) {
|
| DCHECK(sequence_checker_.CalledOnValidSequence());
|
| - NOTIMPLEMENTED();
|
| + if (!change_processor()->IsTrackingMetadata())
|
| + return; // Sync processor not yet ready, don't sync.
|
| +
|
| + // Ignore URLs expired due to old age (we don't want to sync them as deletions
|
| + // to avoid extra traffic up to the server, and also to make sure that a
|
| + // client with a bad clock setting won't go on an expiration rampage and
|
| + // delete all history from every client). The server will gracefully age out
|
| + // the sync DB entries when they've been idle for long enough.
|
| + if (expired)
|
| + return;
|
| +
|
| + std::unique_ptr<MetadataChangeList> metadata_change_list =
|
| + CreateMetadataChangeList();
|
| +
|
| + if (all_history) {
|
| + auto batch = base::MakeUnique<syncer::MetadataBatch>();
|
| + if (!sync_metadata_database_->GetAllSyncMetadata(batch.get())) {
|
| + change_processor()->ReportError(FROM_HERE,
|
| + "Failed reading typed url metadata from "
|
| + "TypedURLSyncMetadataDatabase.");
|
| + return;
|
| + }
|
| +
|
| + syncer::EntityMetadataMap metadata_map(batch->TakeAllMetadata());
|
| + for (const auto& kv : metadata_map) {
|
| + change_processor()->Delete(kv.first, metadata_change_list.get());
|
| + }
|
| + } else {
|
| + // Delete rows.
|
| + for (const auto& row : deleted_rows) {
|
| + std::string storage_key = GetStorageKeyFromURLRow(row);
|
| + change_processor()->Delete(storage_key, metadata_change_list.get());
|
| + }
|
| + }
|
| }
|
|
|
| void TypedURLSyncBridge::Init() {
|
| @@ -275,6 +427,7 @@ int TypedURLSyncBridge::GetErrorPercentage() const {
|
| return num_db_accesses_ ? (100 * num_db_errors_ / num_db_accesses_) : 0;
|
| }
|
|
|
| +// static
|
| bool TypedURLSyncBridge::WriteToTypedUrlSpecifics(
|
| const URLRow& url,
|
| const VisitVector& visits,
|
| @@ -293,11 +446,7 @@ bool TypedURLSyncBridge::WriteToTypedUrlSpecifics(
|
| bool only_typed = false;
|
| int skip_count = 0;
|
|
|
| - if (std::find_if(visits.begin(), visits.end(),
|
| - [](const history::VisitRow& visit) {
|
| - return ui::PageTransitionCoreTypeIs(
|
| - visit.transition, ui::PAGE_TRANSITION_TYPED);
|
| - }) == visits.end()) {
|
| + if (!HasTypedUrl(visits)) {
|
| // This URL has no TYPED visits, don't sync it
|
| return false;
|
| }
|
| @@ -368,10 +517,10 @@ bool TypedURLSyncBridge::WriteToTypedUrlSpecifics(
|
| // static
|
| TypedURLSyncBridge::MergeResult TypedURLSyncBridge::MergeUrls(
|
| const TypedUrlSpecifics& sync_url,
|
| - const history::URLRow& url,
|
| - history::VisitVector* visits,
|
| - history::URLRow* new_url,
|
| - std::vector<history::VisitInfo>* new_visits) {
|
| + const URLRow& url,
|
| + VisitVector* visits,
|
| + URLRow* new_url,
|
| + std::vector<VisitInfo>* new_visits) {
|
| DCHECK(new_url);
|
| DCHECK_EQ(sync_url.url(), url.url().spec());
|
| DCHECK_EQ(sync_url.url(), new_url->url().spec());
|
| @@ -445,7 +594,7 @@ TypedURLSyncBridge::MergeResult TypedURLSyncBridge::MergeUrls(
|
| // check should be removed.
|
| if (sync_url_time > earliest_history_time) {
|
| different |= DIFF_LOCAL_VISITS_ADDED;
|
| - new_visits->push_back(history::VisitInfo(
|
| + new_visits->push_back(VisitInfo(
|
| sync_url_time, ui::PageTransitionFromInt(sync_url.visit_transitions(
|
| sync_url_visit_index))));
|
| }
|
| @@ -464,17 +613,15 @@ TypedURLSyncBridge::MergeResult TypedURLSyncBridge::MergeUrls(
|
| // new visits from the server need to be added to the vector containing
|
| // local visits. These visits will be passed to the server.
|
| // Insert new visits into the appropriate place in the visits vector.
|
| - history::VisitVector::iterator visit_ix = visits->begin();
|
| - for (std::vector<history::VisitInfo>::iterator new_visit =
|
| - new_visits->begin();
|
| + VisitVector::iterator visit_ix = visits->begin();
|
| + for (std::vector<VisitInfo>::iterator new_visit = new_visits->begin();
|
| new_visit != new_visits->end(); ++new_visit) {
|
| while (visit_ix != visits->end() &&
|
| new_visit->first > visit_ix->visit_time) {
|
| ++visit_ix;
|
| }
|
| - visit_ix =
|
| - visits->insert(visit_ix, history::VisitRow(url.id(), new_visit->first,
|
| - 0, new_visit->second, 0));
|
| + visit_ix = visits->insert(visit_ix, VisitRow(url.id(), new_visit->first,
|
| + 0, new_visit->second, 0));
|
| ++visit_ix;
|
| }
|
| }
|
| @@ -484,10 +631,58 @@ TypedURLSyncBridge::MergeResult TypedURLSyncBridge::MergeUrls(
|
| return different;
|
| }
|
|
|
| +// static
|
| +void TypedURLSyncBridge::DiffVisits(
|
| + const VisitVector& history_visits,
|
| + const sync_pb::TypedUrlSpecifics& sync_specifics,
|
| + std::vector<VisitInfo>* new_visits,
|
| + VisitVector* removed_visits) {
|
| + DCHECK(new_visits);
|
| + size_t old_visit_count = history_visits.size();
|
| + size_t new_visit_count = sync_specifics.visits_size();
|
| + size_t old_index = 0;
|
| + size_t new_index = 0;
|
| + while (old_index < old_visit_count && new_index < new_visit_count) {
|
| + base::Time new_visit_time =
|
| + base::Time::FromInternalValue(sync_specifics.visits(new_index));
|
| + if (history_visits[old_index].visit_time < new_visit_time) {
|
| + if (new_index > 0 && removed_visits) {
|
| + // If there are visits missing from the start of the node, that
|
| + // means that they were probably clipped off due to our code that
|
| + // limits the size of the sync nodes - don't delete them from our
|
| + // local history.
|
| + removed_visits->push_back(history_visits[old_index]);
|
| + }
|
| + ++old_index;
|
| + } else if (history_visits[old_index].visit_time > new_visit_time) {
|
| + new_visits->push_back(VisitInfo(
|
| + new_visit_time, ui::PageTransitionFromInt(
|
| + sync_specifics.visit_transitions(new_index))));
|
| + ++new_index;
|
| + } else {
|
| + ++old_index;
|
| + ++new_index;
|
| + }
|
| + }
|
| +
|
| + if (removed_visits) {
|
| + for (; old_index < old_visit_count; ++old_index) {
|
| + removed_visits->push_back(history_visits[old_index]);
|
| + }
|
| + }
|
| +
|
| + for (; new_index < new_visit_count; ++new_index) {
|
| + new_visits->push_back(VisitInfo(
|
| + base::Time::FromInternalValue(sync_specifics.visits(new_index)),
|
| + ui::PageTransitionFromInt(
|
| + sync_specifics.visit_transitions(new_index))));
|
| + }
|
| +}
|
| +
|
| // static
|
| void TypedURLSyncBridge::UpdateURLRowFromTypedUrlSpecifics(
|
| const TypedUrlSpecifics& typed_url,
|
| - history::URLRow* new_url) {
|
| + URLRow* new_url) {
|
| DCHECK_GT(typed_url.visits_size(), 0);
|
| CHECK_EQ(typed_url.visit_transitions_size(), typed_url.visits_size());
|
| if (!new_url->url().is_valid()) {
|
| @@ -525,13 +720,13 @@ void TypedURLSyncBridge::ClearErrorStats() {
|
| num_db_errors_ = 0;
|
| }
|
|
|
| -void TypedURLSyncBridge::UpdateUrlFromServer(
|
| +void TypedURLSyncBridge::MergeURLWithSync(
|
| const sync_pb::TypedUrlSpecifics& server_typed_url,
|
| TypedURLMap* local_typed_urls,
|
| URLVisitVectorMap* local_visit_vectors,
|
| - history::URLRows* new_synced_urls,
|
| + URLRows* new_synced_urls,
|
| TypedURLVisitVector* new_synced_visits,
|
| - history::URLRows* updated_synced_urls) {
|
| + URLRows* updated_synced_urls) {
|
| DCHECK(server_typed_url.visits_size() != 0);
|
| DCHECK_EQ(server_typed_url.visits_size(),
|
| server_typed_url.visit_transitions_size());
|
| @@ -553,7 +748,7 @@ void TypedURLSyncBridge::UpdateUrlFromServer(
|
| TypedURLMap::iterator it = local_typed_urls->find(GURL(sync_url.url()));
|
| if (it == local_typed_urls->end()) {
|
| // There are no matching typed urls from the local db, check for untyped
|
| - history::URLRow untyped_url(GURL(sync_url.url()));
|
| + URLRow untyped_url(GURL(sync_url.url()));
|
|
|
| // The URL may still exist in the local db if it is an untyped url.
|
| // An untyped url will transition to a typed url after receiving visits
|
| @@ -563,7 +758,7 @@ void TypedURLSyncBridge::UpdateUrlFromServer(
|
| history_backend_->GetURL(untyped_url.url(), &untyped_url);
|
| if (is_existing_url) {
|
| // Add a new entry to |local_typed_urls|, and set the iterator to it.
|
| - history::VisitVector untyped_visits;
|
| + VisitVector untyped_visits;
|
| if (!FixupURLAndGetVisits(&untyped_url, &untyped_visits)) {
|
| return;
|
| }
|
| @@ -579,12 +774,12 @@ void TypedURLSyncBridge::UpdateUrlFromServer(
|
| } else {
|
| // The url is new to the local history DB.
|
| // Create new db entry for url.
|
| - history::URLRow new_url(GURL(sync_url.url()));
|
| + URLRow new_url(GURL(sync_url.url()));
|
| UpdateURLRowFromTypedUrlSpecifics(sync_url, &new_url);
|
| new_synced_urls->push_back(new_url);
|
|
|
| // Add entries for url visits.
|
| - std::vector<history::VisitInfo> added_visits;
|
| + std::vector<VisitInfo> added_visits;
|
| size_t visit_count = sync_url.visits_size();
|
|
|
| for (size_t index = 0; index < visit_count; ++index) {
|
| @@ -592,19 +787,18 @@ void TypedURLSyncBridge::UpdateUrlFromServer(
|
| base::Time::FromInternalValue(sync_url.visits(index));
|
| ui::PageTransition transition =
|
| ui::PageTransitionFromInt(sync_url.visit_transitions(index));
|
| - added_visits.push_back(history::VisitInfo(visit_time, transition));
|
| + added_visits.push_back(VisitInfo(visit_time, transition));
|
| }
|
| new_synced_visits->push_back(
|
| - std::pair<GURL, std::vector<history::VisitInfo>>(new_url.url(),
|
| - added_visits));
|
| + std::pair<GURL, std::vector<VisitInfo>>(new_url.url(), added_visits));
|
| return;
|
| }
|
| }
|
|
|
| // Same URL exists in sync data and in history data - compare the
|
| // entries to see if there's any difference.
|
| - history::VisitVector& visits = (*local_visit_vectors)[it->first];
|
| - std::vector<history::VisitInfo> added_visits;
|
| + VisitVector& visits = (*local_visit_vectors)[it->first];
|
| + std::vector<VisitInfo> added_visits;
|
|
|
| // Empty URLs should be filtered out by ShouldIgnoreUrl() previously.
|
| DCHECK(!it->second.url().spec().empty());
|
| @@ -613,7 +807,7 @@ void TypedURLSyncBridge::UpdateUrlFromServer(
|
| // the existing URLRow in the history DB. This is needed because we
|
| // overwrite the existing value in WriteToHistoryBackend(), but some of
|
| // the values in that structure are not synced (like typed_count).
|
| - history::URLRow new_url(it->second);
|
| + URLRow new_url(it->second);
|
|
|
| MergeResult difference =
|
| MergeUrls(sync_url, it->second, &visits, &new_url, &added_visits);
|
| @@ -632,7 +826,7 @@ void TypedURLSyncBridge::UpdateUrlFromServer(
|
| if (sync_url.visits_size() > 0) {
|
| base::Time earliest_visit =
|
| base::Time::FromInternalValue(sync_url.visits(0));
|
| - for (history::VisitVector::iterator i = visits.begin();
|
| + for (VisitVector::iterator i = visits.begin();
|
| i != visits.end() && i->visit_time < earliest_visit;) {
|
| i = visits.erase(i);
|
| }
|
| @@ -652,8 +846,7 @@ void TypedURLSyncBridge::UpdateUrlFromServer(
|
| if (difference & DIFF_LOCAL_VISITS_ADDED) {
|
| // Add entry with new visits to new_synced_visits to update the local db.
|
| new_synced_visits->push_back(
|
| - std::pair<GURL, std::vector<history::VisitInfo>>(it->first,
|
| - added_visits));
|
| + std::pair<GURL, std::vector<VisitInfo>>(it->first, added_visits));
|
| }
|
| } else {
|
| // No difference in urls, erase from map
|
| @@ -661,17 +854,69 @@ void TypedURLSyncBridge::UpdateUrlFromServer(
|
| }
|
| }
|
|
|
| +void TypedURLSyncBridge::UpdateFromSync(
|
| + const sync_pb::TypedUrlSpecifics& typed_url,
|
| + TypedURLVisitVector* visits_to_add,
|
| + VisitVector* visits_to_remove,
|
| + URLRows* updated_urls,
|
| + URLRows* new_urls) {
|
| + URLRow new_url(GURL(typed_url.url()));
|
| + VisitVector existing_visits;
|
| + bool existing_url = history_backend_->GetURL(new_url.url(), &new_url);
|
| + if (existing_url) {
|
| + // This URL already exists locally - fetch the visits so we can
|
| + // merge them below.
|
| + if (!FixupURLAndGetVisits(&new_url, &existing_visits)) {
|
| + return;
|
| + }
|
| + }
|
| + visits_to_add->push_back(std::pair<GURL, std::vector<VisitInfo>>(
|
| + new_url.url(), std::vector<VisitInfo>()));
|
| +
|
| + // Update the URL with information from the typed URL.
|
| + UpdateURLRowFromTypedUrlSpecifics(typed_url, &new_url);
|
| +
|
| + // Figure out which visits we need to add.
|
| + DiffVisits(existing_visits, typed_url, &visits_to_add->back().second,
|
| + visits_to_remove);
|
| +
|
| + if (existing_url) {
|
| + updated_urls->push_back(new_url);
|
| + } else {
|
| + new_urls->push_back(new_url);
|
| + }
|
| +}
|
| +
|
| +void TypedURLSyncBridge::UpdateSyncFromLocal(
|
| + URLRow row,
|
| + MetadataChangeList* metadata_change_list) {
|
| + DCHECK_GE(row.typed_count(), 0);
|
| +
|
| + if (ShouldIgnoreUrl(row.url()))
|
| + return;
|
| +
|
| + // Get the visits for this node.
|
| + VisitVector visit_vector;
|
| + if (!FixupURLAndGetVisits(&row, &visit_vector)) {
|
| + return;
|
| + }
|
| +
|
| + SendTypedURLToProcessor(row, visit_vector, metadata_change_list);
|
| +
|
| + return;
|
| +}
|
| +
|
| base::Optional<ModelError> TypedURLSyncBridge::WriteToHistoryBackend(
|
| - const history::URLRows* new_urls,
|
| - const history::URLRows* updated_urls,
|
| + const URLRows* new_urls,
|
| + const URLRows* updated_urls,
|
| const std::vector<GURL>* deleted_urls,
|
| const TypedURLVisitVector* new_visits,
|
| - const history::VisitVector* deleted_visits) {
|
| + const VisitVector* deleted_visits) {
|
| if (deleted_urls && !deleted_urls->empty())
|
| history_backend_->DeleteURLs(*deleted_urls);
|
|
|
| if (new_urls) {
|
| - history_backend_->AddPagesWithDetails(*new_urls, history::SOURCE_SYNCED);
|
| + history_backend_->AddPagesWithDetails(*new_urls, SOURCE_SYNCED);
|
| }
|
|
|
| if (updated_urls) {
|
| @@ -694,7 +939,7 @@ base::Optional<ModelError> TypedURLSyncBridge::WriteToHistoryBackend(
|
| continue;
|
| ++num_db_accesses_;
|
| if (!history_backend_->AddVisits(visits.first, visits.second,
|
| - history::SOURCE_SYNCED)) {
|
| + SOURCE_SYNCED)) {
|
| ++num_db_errors_;
|
| return ModelError(FROM_HERE, "Could not add visits to HistoryBackend.");
|
| }
|
| @@ -748,7 +993,7 @@ bool TypedURLSyncBridge::ShouldIgnoreUrl(const GURL& url) {
|
| if (net::IsLocalhost(url.host_piece()))
|
| return true;
|
|
|
| - // Ignore username and password, sonce history backend will remove user name
|
| + // Ignore username and password, since history backend will remove user name
|
| // and password in URLDatabase::GURLToDatabaseURL and send username/password
|
| // removed url to sync later.
|
| if (url.has_username() || url.has_password())
|
| @@ -757,12 +1002,11 @@ bool TypedURLSyncBridge::ShouldIgnoreUrl(const GURL& url) {
|
| return false;
|
| }
|
|
|
| -bool TypedURLSyncBridge::ShouldIgnoreVisits(
|
| - const history::VisitVector& visits) {
|
| +bool TypedURLSyncBridge::ShouldIgnoreVisits(const VisitVector& visits) {
|
| // We ignore URLs that were imported, but have never been visited by
|
| // chromium.
|
| - static const int kFirstImportedSource = history::SOURCE_FIREFOX_IMPORTED;
|
| - history::VisitSourceMap map;
|
| + static const int kFirstImportedSource = SOURCE_FIREFOX_IMPORTED;
|
| + VisitSourceMap map;
|
| if (!history_backend_->GetVisitsSource(visits, &map))
|
| return false; // If we can't read the visit, assume it's not imported.
|
|
|
| @@ -777,6 +1021,21 @@ bool TypedURLSyncBridge::ShouldIgnoreVisits(
|
| return true;
|
| }
|
|
|
| +bool TypedURLSyncBridge::ShouldSyncVisit(int typed_count,
|
| + ui::PageTransition transition) {
|
| + // Just use an ad-hoc criteria to determine whether to ignore this
|
| + // notification. For most users, the distribution of visits is roughly a bell
|
| + // curve with a long tail - there are lots of URLs with < 5 visits so we want
|
| + // to make sure we sync up every visit to ensure the proper ordering of
|
| + // suggestions. But there are relatively few URLs with > 10 visits, and those
|
| + // tend to be more broadly distributed such that there's no need to sync up
|
| + // every visit to preserve their relative ordering.
|
| + return (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED) &&
|
| + typed_count >= 0 &&
|
| + (typed_count < kTypedUrlVisitThrottleThreshold ||
|
| + (typed_count % kTypedUrlVisitThrottleMultiple) == 0));
|
| +}
|
| +
|
| bool TypedURLSyncBridge::FixupURLAndGetVisits(URLRow* url,
|
| VisitVector* visits) {
|
| ++num_db_accesses_;
|
| @@ -852,10 +1111,9 @@ std::unique_ptr<EntityData> TypedURLSyncBridge::CreateEntityData(
|
|
|
| if (!WriteToTypedUrlSpecifics(row, visits, specifics)) {
|
| // Cannot write to specifics, ex. no TYPED visits.
|
| - return base::MakeUnique<EntityData>();
|
| + return nullptr;
|
| }
|
| entity_data->non_unique_name = row.url().spec();
|
| -
|
| return entity_data;
|
| }
|
|
|
| @@ -864,13 +1122,13 @@ bool TypedURLSyncBridge::GetValidURLsAndVisits(URLVisitVectorMap* url_to_visit,
|
| DCHECK(url_to_visit);
|
| DCHECK(url_to_urlrow);
|
|
|
| - history::URLRows local_typed_urls;
|
| + URLRows local_typed_urls;
|
| ++num_db_accesses_;
|
| if (!history_backend_->GetAllTypedURLs(&local_typed_urls)) {
|
| ++num_db_errors_;
|
| return false;
|
| }
|
| - for (history::URLRow& url : local_typed_urls) {
|
| + for (URLRow& url : local_typed_urls) {
|
| DCHECK_EQ(0U, url_to_visit->count(url.url()));
|
| if (!FixupURLAndGetVisits(&url, &((*url_to_visit)[url.url()])) ||
|
| ShouldIgnoreUrl(url.url()) ||
|
| @@ -901,4 +1159,23 @@ std::string TypedURLSyncBridge::GetStorageKeyInternal(const std::string& url) {
|
| return GetStorageKeyFromURLRow(existing_url);
|
| }
|
|
|
| +void TypedURLSyncBridge::SendTypedURLToProcessor(
|
| + const URLRow& row,
|
| + const VisitVector& visits,
|
| + MetadataChangeList* metadata_change_list) {
|
| + DCHECK(!visits.empty());
|
| + DCHECK(metadata_change_list);
|
| +
|
| + std::unique_ptr<syncer::EntityData> entity_data =
|
| + CreateEntityData(row, visits);
|
| + if (!entity_data.get()) {
|
| + // Cannot create EntityData, ex. no TYPED visits.
|
| + return;
|
| + }
|
| +
|
| + std::string storage_key = GetStorageKeyFromURLRow(row);
|
| + change_processor()->Put(storage_key, std::move(entity_data),
|
| + metadata_change_list);
|
| +}
|
| +
|
| } // namespace history
|
|
|