Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/history/core/browser/typed_url_sync_bridge.h" | 5 #include "components/history/core/browser/typed_url_sync_bridge.h" |
| 6 | 6 |
| 7 #include <unordered_set> | |
|
pavely
2017/07/14 19:15:58
Why do you need unordered set?
Gang Wu
2017/07/17 18:59:37
Done.
| |
| 8 | |
| 7 #include "base/big_endian.h" | 9 #include "base/big_endian.h" |
| 8 #include "base/memory/ptr_util.h" | 10 #include "base/memory/ptr_util.h" |
| 9 #include "base/metrics/histogram_macros.h" | 11 #include "base/metrics/histogram_macros.h" |
| 10 #include "base/strings/utf_string_conversions.h" | 12 #include "base/strings/utf_string_conversions.h" |
| 11 #include "components/history/core/browser/history_backend.h" | 13 #include "components/history/core/browser/history_backend.h" |
| 12 #include "components/sync/model/mutable_data_batch.h" | 14 #include "components/sync/model/mutable_data_batch.h" |
| 13 #include "components/sync/model_impl/sync_metadata_store_change_list.h" | 15 #include "components/sync/model_impl/sync_metadata_store_change_list.h" |
| 14 #include "net/base/url_util.h" | 16 #include "net/base/url_util.h" |
| 15 | 17 |
| 16 using sync_pb::TypedUrlSpecifics; | 18 using sync_pb::TypedUrlSpecifics; |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 29 // the size under control we limit the visit array. | 31 // the size under control we limit the visit array. |
| 30 static const int kMaxTypedUrlVisits = 100; | 32 static const int kMaxTypedUrlVisits = 100; |
| 31 | 33 |
| 32 // There's no limit on how many visits the history DB could have for a given | 34 // There's no limit on how many visits the history DB could have for a given |
| 33 // typed URL, so we limit how many we fetch from the DB to avoid crashes due to | 35 // typed URL, so we limit how many we fetch from the DB to avoid crashes due to |
| 34 // running out of memory (http://crbug.com/89793). This value is different | 36 // running out of memory (http://crbug.com/89793). This value is different |
| 35 // from kMaxTypedUrlVisits, as some of the visits fetched from the DB may be | 37 // from kMaxTypedUrlVisits, as some of the visits fetched from the DB may be |
| 36 // RELOAD visits, which will be stripped. | 38 // RELOAD visits, which will be stripped. |
| 37 static const int kMaxVisitsToFetch = 1000; | 39 static const int kMaxVisitsToFetch = 1000; |
| 38 | 40 |
| 41 // This is the threshold at which we start throttling sync updates for typed | |
| 42 // URLs - any URLs with a typed_count >= this threshold will be throttled. | |
| 43 static const int kTypedUrlVisitThrottleThreshold = 10; | |
| 44 | |
| 45 // This is the multiple we use when throttling sync updates. If the multiple is | |
| 46 // N, we sync up every Nth update (i.e. when typed_count % N == 0). | |
| 47 static const int kTypedUrlVisitThrottleMultiple = 10; | |
| 48 | |
| 39 // Enforce oldest to newest visit order. | 49 // Enforce oldest to newest visit order. |
| 40 static bool CheckVisitOrdering(const VisitVector& visits) { | 50 static bool CheckVisitOrdering(const VisitVector& visits) { |
| 41 int64_t previous_visit_time = 0; | 51 int64_t previous_visit_time = 0; |
| 42 for (VisitVector::const_iterator visit = visits.begin(); | 52 for (VisitVector::const_iterator visit = visits.begin(); |
| 43 visit != visits.end(); ++visit) { | 53 visit != visits.end(); ++visit) { |
| 44 if (visit != visits.begin() && | 54 if (visit != visits.begin() && |
| 45 previous_visit_time > visit->visit_time.ToInternalValue()) | 55 previous_visit_time > visit->visit_time.ToInternalValue()) |
| 46 return false; | 56 return false; |
| 47 | 57 |
| 48 previous_visit_time = visit->visit_time.ToInternalValue(); | 58 previous_visit_time = visit->visit_time.ToInternalValue(); |
| 49 } | 59 } |
| 50 return true; | 60 return true; |
| 51 } | 61 } |
| 52 | 62 |
| 53 std::string GetStorageKeyFromURLRow(const URLRow& row) { | 63 std::string GetStorageKeyFromURLRow(const URLRow& row) { |
| 64 DCHECK_NE(row.id(), 0); | |
| 54 std::string storage_key(sizeof(row.id()), 0); | 65 std::string storage_key(sizeof(row.id()), 0); |
| 55 base::WriteBigEndian<URLID>(&storage_key[0], row.id()); | 66 base::WriteBigEndian<URLID>(&storage_key[0], row.id()); |
| 56 return storage_key; | 67 return storage_key; |
| 57 } | 68 } |
| 58 | 69 |
| 59 bool HasTypedUrl(const VisitVector& visits) { | 70 bool HasTypedUrl(const VisitVector& visits) { |
| 60 auto typed_url_visit = std::find_if( | 71 auto typed_url_visit = |
| 61 visits.begin(), visits.end(), [](const history::VisitRow& visit) { | 72 std::find_if(visits.begin(), visits.end(), [](const VisitRow& visit) { |
| 62 return ui::PageTransitionCoreTypeIs(visit.transition, | 73 return ui::PageTransitionCoreTypeIs(visit.transition, |
| 63 ui::PAGE_TRANSITION_TYPED); | 74 ui::PAGE_TRANSITION_TYPED); |
| 64 }); | 75 }); |
| 65 return typed_url_visit != visits.end(); | 76 return typed_url_visit != visits.end(); |
| 66 } | 77 } |
| 67 | 78 |
| 68 } // namespace | 79 } // namespace |
| 69 | 80 |
| 70 TypedURLSyncBridge::TypedURLSyncBridge( | 81 TypedURLSyncBridge::TypedURLSyncBridge( |
| 71 HistoryBackend* history_backend, | 82 HistoryBackend* history_backend, |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 91 return base::MakeUnique<syncer::SyncMetadataStoreChangeList>( | 102 return base::MakeUnique<syncer::SyncMetadataStoreChangeList>( |
| 92 sync_metadata_database_, syncer::TYPED_URLS); | 103 sync_metadata_database_, syncer::TYPED_URLS); |
| 93 } | 104 } |
| 94 | 105 |
| 95 base::Optional<ModelError> TypedURLSyncBridge::MergeSyncData( | 106 base::Optional<ModelError> TypedURLSyncBridge::MergeSyncData( |
| 96 std::unique_ptr<MetadataChangeList> metadata_change_list, | 107 std::unique_ptr<MetadataChangeList> metadata_change_list, |
| 97 EntityChangeList entity_data) { | 108 EntityChangeList entity_data) { |
| 98 DCHECK(sequence_checker_.CalledOnValidSequence()); | 109 DCHECK(sequence_checker_.CalledOnValidSequence()); |
| 99 | 110 |
| 100 // Create a mapping of all local data by URLID. These will be narrowed down | 111 // Create a mapping of all local data by URLID. These will be narrowed down |
| 101 // by CreateOrUpdateUrl() to include only the entries different from sync | 112 // by MergeURLWithSync() to include only the entries different from sync |
| 102 // server data. | 113 // server data. |
| 103 TypedURLMap new_db_urls; | 114 TypedURLMap new_db_urls; |
| 104 | 115 |
| 105 // Get all the visits and map the URLRows by URL. | 116 // Get all the visits and map the URLRows by URL. |
| 106 URLVisitVectorMap local_visit_vectors; | 117 URLVisitVectorMap local_visit_vectors; |
| 107 | 118 |
| 108 if (!GetValidURLsAndVisits(&local_visit_vectors, &new_db_urls)) { | 119 if (!GetValidURLsAndVisits(&local_visit_vectors, &new_db_urls)) { |
| 109 return ModelError( | 120 return ModelError( |
| 110 FROM_HERE, "Could not get the typed_url entries from HistoryBackend."); | 121 FROM_HERE, "Could not get the typed_url entries from HistoryBackend."); |
| 111 } | 122 } |
| 112 | 123 |
| 113 // New sync data organized for different write operations to history backend. | 124 // New sync data organized for different write operations to history backend. |
| 114 history::URLRows new_synced_urls; | 125 URLRows new_synced_urls; |
| 115 history::URLRows updated_synced_urls; | 126 URLRows updated_synced_urls; |
| 116 TypedURLVisitVector new_synced_visits; | 127 TypedURLVisitVector new_synced_visits; |
| 117 | 128 |
| 118 // Iterate through entity_data and check for all the urls that | 129 // Iterate through entity_data and check for all the urls that |
| 119 // sync already knows about. CreateOrUpdateUrl() will remove urls that | 130 // sync already knows about. MergeURLWithSync() will remove urls that |
| 120 // are the same as the synced ones from |new_db_urls|. | 131 // are the same as the synced ones from |new_db_urls|. |
| 121 for (const EntityChange& entity_change : entity_data) { | 132 for (const EntityChange& entity_change : entity_data) { |
| 122 DCHECK(entity_change.data().specifics.has_typed_url()); | 133 DCHECK(entity_change.data().specifics.has_typed_url()); |
| 123 const TypedUrlSpecifics& specifics = | 134 const TypedUrlSpecifics& specifics = |
| 124 entity_change.data().specifics.typed_url(); | 135 entity_change.data().specifics.typed_url(); |
| 125 if (ShouldIgnoreUrl(GURL(specifics.url()))) | 136 if (ShouldIgnoreUrl(GURL(specifics.url()))) |
| 126 continue; | 137 continue; |
| 127 | 138 |
| 128 // Ignore old sync urls that don't have any transition data stored with | 139 // Ignore old sync urls that don't have any transition data stored with |
| 129 // them, or transition data that does not match the visit data (will be | 140 // them, or transition data that does not match the visit data (will be |
| 130 // deleted below). | 141 // deleted below). |
| 131 if (specifics.visit_transitions_size() == 0 || | 142 if (specifics.visit_transitions_size() == 0 || |
| 132 specifics.visit_transitions_size() != specifics.visits_size()) { | 143 specifics.visit_transitions_size() != specifics.visits_size()) { |
| 133 // Generate a debug assertion to help track down http://crbug.com/91473, | 144 // Generate a debug assertion to help track down http://crbug.com/91473, |
| 134 // even though we gracefully handle this case by overwriting this node. | 145 // even though we gracefully handle this case by overwriting this node. |
| 135 DCHECK_EQ(specifics.visits_size(), specifics.visit_transitions_size()); | 146 DCHECK_EQ(specifics.visits_size(), specifics.visit_transitions_size()); |
| 136 DVLOG(1) << "Ignoring obsolete sync url with no visit transition info."; | 147 DVLOG(1) << "Ignoring obsolete sync url with no visit transition info."; |
| 137 | 148 |
| 138 continue; | 149 continue; |
| 139 } | 150 } |
| 140 UpdateUrlFromServer(specifics, &new_db_urls, &local_visit_vectors, | 151 MergeURLWithSync(specifics, &new_db_urls, &local_visit_vectors, |
| 141 &new_synced_urls, &new_synced_visits, | 152 &new_synced_urls, &new_synced_visits, |
| 142 &updated_synced_urls); | 153 &updated_synced_urls); |
| 143 } | 154 } |
| 144 | 155 |
| 145 for (const auto& kv : new_db_urls) { | 156 for (const auto& kv : new_db_urls) { |
| 146 if (!HasTypedUrl(local_visit_vectors[kv.first])) { | 157 SendTypedURLToProcessor(kv.second, local_visit_vectors[kv.first], |
| 147 // This URL has no TYPED visits, don't sync it | 158 metadata_change_list.get()); |
| 148 continue; | |
| 149 } | |
| 150 std::string storage_key = GetStorageKeyFromURLRow(kv.second); | |
| 151 change_processor()->Put( | |
| 152 storage_key, CreateEntityData(kv.second, local_visit_vectors[kv.first]), | |
| 153 metadata_change_list.get()); | |
| 154 } | 159 } |
| 155 | 160 |
| 156 base::Optional<ModelError> error = WriteToHistoryBackend( | 161 base::Optional<ModelError> error = WriteToHistoryBackend( |
| 157 &new_synced_urls, &updated_synced_urls, NULL, &new_synced_visits, NULL); | 162 &new_synced_urls, &updated_synced_urls, NULL, &new_synced_visits, NULL); |
| 158 | 163 |
| 159 if (error) | 164 if (error) |
| 160 return error; | 165 return error; |
| 161 | 166 |
| 162 for (const EntityChange& entity_change : entity_data) { | 167 for (const EntityChange& entity_change : entity_data) { |
| 163 DCHECK(entity_change.data().specifics.has_typed_url()); | 168 DCHECK(entity_change.data().specifics.has_typed_url()); |
| 164 std::string storage_key = | 169 std::string storage_key = |
| 165 GetStorageKeyInternal(entity_change.data().specifics.typed_url().url()); | 170 GetStorageKeyInternal(entity_change.data().specifics.typed_url().url()); |
| 166 if (storage_key.empty()) { | 171 if (storage_key.empty()) { |
| 167 // ignore entity change | 172 // ignore entity change |
| 173 change_processor()->UntrackEntity(entity_change.data()); | |
| 168 } else { | 174 } else { |
| 169 change_processor()->UpdateStorageKey(entity_change.data(), storage_key, | 175 change_processor()->UpdateStorageKey(entity_change.data(), storage_key, |
| 170 metadata_change_list.get()); | 176 metadata_change_list.get()); |
| 171 } | 177 } |
| 172 } | 178 } |
| 173 | 179 |
| 174 UMA_HISTOGRAM_PERCENTAGE("Sync.TypedUrlMergeAndStartSyncingErrors", | 180 UMA_HISTOGRAM_PERCENTAGE("Sync.TypedUrlMergeAndStartSyncingErrors", |
| 175 GetErrorPercentage()); | 181 GetErrorPercentage()); |
| 176 ClearErrorStats(); | 182 ClearErrorStats(); |
| 177 | 183 |
| 178 return static_cast<syncer::SyncMetadataStoreChangeList*>( | 184 return static_cast<syncer::SyncMetadataStoreChangeList*>( |
| 179 metadata_change_list.get()) | 185 metadata_change_list.get()) |
| 180 ->TakeError(); | 186 ->TakeError(); |
| 181 } | 187 } |
| 182 | 188 |
| 183 base::Optional<ModelError> TypedURLSyncBridge::ApplySyncChanges( | 189 base::Optional<ModelError> TypedURLSyncBridge::ApplySyncChanges( |
| 184 std::unique_ptr<MetadataChangeList> metadata_change_list, | 190 std::unique_ptr<MetadataChangeList> metadata_change_list, |
| 185 EntityChangeList entity_changes) { | 191 EntityChangeList entity_changes) { |
| 186 DCHECK(sequence_checker_.CalledOnValidSequence()); | 192 DCHECK(sequence_checker_.CalledOnValidSequence()); |
| 187 NOTIMPLEMENTED(); | 193 DCHECK(sync_metadata_database_); |
| 194 | |
| 195 std::vector<GURL> pending_deleted_urls; | |
| 196 TypedURLVisitVector new_synced_visits; | |
| 197 VisitVector deleted_visits; | |
| 198 URLRows updated_synced_urls; | |
| 199 URLRows new_synced_urls; | |
| 200 | |
| 201 for (const EntityChange& entity_change : entity_changes) { | |
| 202 if (entity_change.type() == EntityChange::ACTION_DELETE) { | |
| 203 URLRow url_row; | |
| 204 int64_t url_id = sync_metadata_database_->StorageKeyToURLID( | |
| 205 entity_change.storage_key()); | |
| 206 if (!history_backend_->GetURLByID(url_id, &url_row)) { | |
| 207 // Ignoring the case that there is no matching URLRow with URLID | |
| 208 // |url_id|. | |
| 209 continue; | |
| 210 } | |
| 211 | |
| 212 pending_deleted_urls.push_back(url_row.url()); | |
| 213 continue; | |
| 214 } | |
| 215 | |
| 216 DCHECK(entity_change.data().specifics.has_typed_url()); | |
| 217 const TypedUrlSpecifics& specifics = | |
| 218 entity_change.data().specifics.typed_url(); | |
| 219 | |
| 220 GURL url(specifics.url()); | |
| 221 | |
| 222 if (ShouldIgnoreUrl(url)) | |
| 223 continue; | |
| 224 | |
| 225 DCHECK(specifics.visits_size()); | |
| 226 sync_pb::TypedUrlSpecifics filtered_url = FilterExpiredVisits(specifics); | |
| 227 if (filtered_url.visits_size() == 0) | |
| 228 continue; | |
| 229 | |
| 230 UpdateFromSync(filtered_url, &new_synced_visits, &deleted_visits, | |
| 231 &updated_synced_urls, &new_synced_urls); | |
| 232 } | |
| 233 | |
| 234 WriteToHistoryBackend(&new_synced_urls, &updated_synced_urls, | |
| 235 &pending_deleted_urls, &new_synced_visits, | |
| 236 &deleted_visits); | |
| 237 | |
| 238 // Finally, update storage key in processor. | |
|
pavely
2017/07/14 19:15:57
// New entities were either ignored or written to
Gang Wu
2017/07/17 18:59:37
Done.
| |
| 239 for (const EntityChange& entity_change : entity_changes) { | |
| 240 if (entity_change.type() == EntityChange::ACTION_ADD) { | |
| 241 std::string storage_key = GetStorageKeyInternal( | |
| 242 entity_change.data().specifics.typed_url().url()); | |
| 243 if (storage_key.empty()) { | |
| 244 // ignore entity change | |
| 245 change_processor()->UntrackEntity(entity_change.data()); | |
| 246 } else { | |
| 247 change_processor()->UpdateStorageKey(entity_change.data(), storage_key, | |
| 248 metadata_change_list.get()); | |
| 249 } | |
| 250 } | |
| 251 } | |
| 252 | |
| 188 return {}; | 253 return {}; |
| 189 } | 254 } |
| 190 | 255 |
| 191 void TypedURLSyncBridge::GetData(StorageKeyList storage_keys, | 256 void TypedURLSyncBridge::GetData(StorageKeyList storage_keys, |
| 192 DataCallback callback) { | 257 DataCallback callback) { |
| 193 DCHECK(sequence_checker_.CalledOnValidSequence()); | 258 DCHECK(sequence_checker_.CalledOnValidSequence()); |
| 194 NOTIMPLEMENTED(); | 259 DCHECK(sync_metadata_database_); |
| 260 | |
| 261 auto batch = base::MakeUnique<MutableDataBatch>(); | |
| 262 for (const std::string& key : storage_keys) { | |
| 263 URLRow url_row; | |
| 264 URLID url_id = sync_metadata_database_->StorageKeyToURLID(key); | |
| 265 | |
| 266 ++num_db_accesses_; | |
| 267 if (!history_backend_->GetURLByID(url_id, &url_row)) { | |
| 268 // Ignoring the case which no matching URLRow with URLID |url_id|. | |
| 269 DLOG(ERROR) << "Could not find URL for id: " << url_id; | |
| 270 continue; | |
| 271 } | |
| 272 | |
| 273 VisitVector visits_vector; | |
| 274 FixupURLAndGetVisits(&url_row, &visits_vector); | |
| 275 batch->Put(key, CreateEntityData(url_row, visits_vector)); | |
| 276 } | |
| 277 | |
| 278 callback.Run(std::move(batch)); | |
| 195 } | 279 } |
| 196 | 280 |
| 197 void TypedURLSyncBridge::GetAllData(DataCallback callback) { | 281 void TypedURLSyncBridge::GetAllData(DataCallback callback) { |
| 198 DCHECK(sequence_checker_.CalledOnValidSequence()); | 282 DCHECK(sequence_checker_.CalledOnValidSequence()); |
| 199 | 283 |
| 200 history::URLRows typed_urls; | 284 URLRows typed_urls; |
| 201 ++num_db_accesses_; | 285 ++num_db_accesses_; |
| 202 if (!history_backend_->GetAllTypedURLs(&typed_urls)) { | 286 if (!history_backend_->GetAllTypedURLs(&typed_urls)) { |
| 203 ++num_db_errors_; | 287 ++num_db_errors_; |
| 204 change_processor()->ReportError(FROM_HERE, | 288 change_processor()->ReportError(FROM_HERE, |
| 205 "Could not get the typed_url entries."); | 289 "Could not get the typed_url entries."); |
| 206 return; | 290 return; |
| 207 } | 291 } |
| 208 | 292 |
| 209 auto batch = base::MakeUnique<MutableDataBatch>(); | 293 auto batch = base::MakeUnique<MutableDataBatch>(); |
| 210 for (history::URLRow& url : typed_urls) { | 294 for (URLRow& url : typed_urls) { |
| 211 VisitVector visits_vector; | 295 VisitVector visits_vector; |
| 212 FixupURLAndGetVisits(&url, &visits_vector); | 296 FixupURLAndGetVisits(&url, &visits_vector); |
| 213 batch->Put(GetStorageKeyFromURLRow(url), | 297 batch->Put(GetStorageKeyFromURLRow(url), |
| 214 CreateEntityData(url, visits_vector)); | 298 CreateEntityData(url, visits_vector)); |
| 215 } | 299 } |
| 216 callback.Run(std::move(batch)); | 300 callback.Run(std::move(batch)); |
| 217 } | 301 } |
| 218 | 302 |
| 219 // Must be exactly the value of GURL::spec() for backwards comparability with | 303 // Must be exactly the value of GURL::spec() for backwards comparability with |
| 220 // the previous (Directory + SyncableService) iteration of sync integration. | 304 // the previous (Directory + SyncableService) iteration of sync integration. |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 232 // with sync because it has a significantly low memory cost than a URL. | 316 // with sync because it has a significantly low memory cost than a URL. |
| 233 std::string TypedURLSyncBridge::GetStorageKey(const EntityData& entity_data) { | 317 std::string TypedURLSyncBridge::GetStorageKey(const EntityData& entity_data) { |
| 234 NOTREACHED() << "TypedURLSyncBridge do not support GetStorageKey."; | 318 NOTREACHED() << "TypedURLSyncBridge do not support GetStorageKey."; |
| 235 return std::string(); | 319 return std::string(); |
| 236 } | 320 } |
| 237 | 321 |
| 238 bool TypedURLSyncBridge::SupportsGetStorageKey() const { | 322 bool TypedURLSyncBridge::SupportsGetStorageKey() const { |
| 239 return false; | 323 return false; |
| 240 } | 324 } |
| 241 | 325 |
| 242 void TypedURLSyncBridge::OnURLVisited(history::HistoryBackend* history_backend, | 326 void TypedURLSyncBridge::OnURLVisited(HistoryBackend* history_backend, |
| 243 ui::PageTransition transition, | 327 ui::PageTransition transition, |
| 244 const history::URLRow& row, | 328 const URLRow& row, |
| 245 const history::RedirectList& redirects, | 329 const RedirectList& redirects, |
| 246 base::Time visit_time) { | 330 base::Time visit_time) { |
| 247 DCHECK(sequence_checker_.CalledOnValidSequence()); | 331 DCHECK(sequence_checker_.CalledOnValidSequence()); |
| 248 NOTIMPLEMENTED(); | 332 |
| 333 if (!change_processor()->IsTrackingMetadata()) | |
| 334 return; // Sync processor not yet ready, don't sync. | |
| 335 if (!ShouldSyncVisit(row.typed_count(), transition)) | |
| 336 return; | |
| 337 | |
| 338 std::unique_ptr<MetadataChangeList> metadata_change_list = | |
| 339 CreateMetadataChangeList(); | |
| 340 | |
| 341 UpdateSyncFromLocal(row, metadata_change_list.get()); | |
| 249 } | 342 } |
| 250 | 343 |
| 251 void TypedURLSyncBridge::OnURLsModified( | 344 void TypedURLSyncBridge::OnURLsModified(HistoryBackend* history_backend, |
| 252 history::HistoryBackend* history_backend, | 345 const URLRows& changed_urls) { |
| 253 const history::URLRows& changed_urls) { | |
| 254 DCHECK(sequence_checker_.CalledOnValidSequence()); | 346 DCHECK(sequence_checker_.CalledOnValidSequence()); |
| 255 NOTIMPLEMENTED(); | 347 |
| 348 if (!change_processor()->IsTrackingMetadata()) | |
| 349 return; // Sync processor not yet ready, don't sync. | |
| 350 | |
| 351 std::unique_ptr<MetadataChangeList> metadata_change_list = | |
| 352 CreateMetadataChangeList(); | |
| 353 | |
| 354 for (const auto& row : changed_urls) { | |
| 355 // Only care if the modified URL is typed. | |
| 356 if (row.typed_count() >= 0) { | |
| 357 // If there were any errors updating the sync node, just ignore them and | |
| 358 // continue on to process the next URL. | |
| 359 UpdateSyncFromLocal(row, metadata_change_list.get()); | |
| 360 } | |
| 361 } | |
| 256 } | 362 } |
| 257 | 363 |
| 258 void TypedURLSyncBridge::OnURLsDeleted(history::HistoryBackend* history_backend, | 364 void TypedURLSyncBridge::OnURLsDeleted(HistoryBackend* history_backend, |
| 259 bool all_history, | 365 bool all_history, |
| 260 bool expired, | 366 bool expired, |
| 261 const history::URLRows& deleted_rows, | 367 const URLRows& deleted_rows, |
| 262 const std::set<GURL>& favicon_urls) { | 368 const std::set<GURL>& favicon_urls) { |
| 263 DCHECK(sequence_checker_.CalledOnValidSequence()); | 369 DCHECK(sequence_checker_.CalledOnValidSequence()); |
| 264 NOTIMPLEMENTED(); | 370 if (!change_processor()) |
|
pavely
2017/07/14 19:15:57
Same here.
Gang Wu
2017/07/17 18:59:37
Done.
| |
| 371 return; // Sync processor not yet initialized, don't sync. | |
| 372 | |
| 373 // Ignore URLs expired due to old age (we don't want to sync them as deletions | |
| 374 // to avoid extra traffic up to the server, and also to make sure that a | |
| 375 // client with a bad clock setting won't go on an expiration rampage and | |
| 376 // delete all history from every client). The server will gracefully age out | |
| 377 // the sync DB entries when they've been idle for long enough. | |
| 378 if (expired) | |
| 379 return; | |
| 380 | |
| 381 std::unique_ptr<MetadataChangeList> metadata_change_list = | |
| 382 CreateMetadataChangeList(); | |
| 383 | |
| 384 if (all_history) { | |
| 385 auto batch = base::MakeUnique<syncer::MetadataBatch>(); | |
| 386 if (!sync_metadata_database_->GetAllSyncMetadata(batch.get())) { | |
| 387 change_processor()->ReportError(FROM_HERE, | |
| 388 "Failed reading typed url metadata from " | |
| 389 "TypedURLSyncMetadataDatabase."); | |
| 390 return; | |
| 391 } | |
| 392 | |
| 393 syncer::EntityMetadataMap metadata_map(batch->TakeAllMetadata()); | |
| 394 for (const auto& kv : metadata_map) { | |
| 395 change_processor()->Delete(kv.first, metadata_change_list.get()); | |
| 396 } | |
| 397 } else { | |
| 398 // Delete rows. | |
| 399 for (const auto& row : deleted_rows) { | |
| 400 std::string storage_key = GetStorageKeyFromURLRow(row); | |
| 401 change_processor()->Delete(storage_key, metadata_change_list.get()); | |
| 402 } | |
| 403 } | |
| 265 } | 404 } |
| 266 | 405 |
| 267 void TypedURLSyncBridge::Init() { | 406 void TypedURLSyncBridge::Init() { |
| 268 DCHECK(sequence_checker_.CalledOnValidSequence()); | 407 DCHECK(sequence_checker_.CalledOnValidSequence()); |
| 269 | 408 |
| 270 history_backend_observer_.Add(history_backend_); | 409 history_backend_observer_.Add(history_backend_); |
| 271 LoadMetadata(); | 410 LoadMetadata(); |
| 272 } | 411 } |
| 273 | 412 |
| 274 int TypedURLSyncBridge::GetErrorPercentage() const { | 413 int TypedURLSyncBridge::GetErrorPercentage() const { |
| 275 return num_db_accesses_ ? (100 * num_db_errors_ / num_db_accesses_) : 0; | 414 return num_db_accesses_ ? (100 * num_db_errors_ / num_db_accesses_) : 0; |
| 276 } | 415 } |
| 277 | 416 |
| 417 // static | |
| 278 bool TypedURLSyncBridge::WriteToTypedUrlSpecifics( | 418 bool TypedURLSyncBridge::WriteToTypedUrlSpecifics( |
| 279 const URLRow& url, | 419 const URLRow& url, |
| 280 const VisitVector& visits, | 420 const VisitVector& visits, |
| 281 TypedUrlSpecifics* typed_url) { | 421 TypedUrlSpecifics* typed_url) { |
| 282 DCHECK(!url.last_visit().is_null()); | 422 DCHECK(!url.last_visit().is_null()); |
| 283 DCHECK(!visits.empty()); | 423 DCHECK(!visits.empty()); |
| 284 DCHECK_EQ(url.last_visit().ToInternalValue(), | 424 DCHECK_EQ(url.last_visit().ToInternalValue(), |
| 285 visits.back().visit_time.ToInternalValue()); | 425 visits.back().visit_time.ToInternalValue()); |
| 286 | 426 |
| 287 typed_url->set_url(url.url().spec()); | 427 typed_url->set_url(url.url().spec()); |
| 288 typed_url->set_title(base::UTF16ToUTF8(url.title())); | 428 typed_url->set_title(base::UTF16ToUTF8(url.title())); |
| 289 typed_url->set_hidden(url.hidden()); | 429 typed_url->set_hidden(url.hidden()); |
| 290 | 430 |
| 291 DCHECK(CheckVisitOrdering(visits)); | 431 DCHECK(CheckVisitOrdering(visits)); |
| 292 | 432 |
| 293 bool only_typed = false; | 433 bool only_typed = false; |
| 294 int skip_count = 0; | 434 int skip_count = 0; |
| 295 | 435 |
| 296 if (std::find_if(visits.begin(), visits.end(), | 436 if (!HasTypedUrl(visits)) { |
| 297 [](const history::VisitRow& visit) { | |
| 298 return ui::PageTransitionCoreTypeIs( | |
| 299 visit.transition, ui::PAGE_TRANSITION_TYPED); | |
| 300 }) == visits.end()) { | |
| 301 // This URL has no TYPED visits, don't sync it | 437 // This URL has no TYPED visits, don't sync it |
| 302 return false; | 438 return false; |
| 303 } | 439 } |
| 304 | 440 |
| 305 if (visits.size() > static_cast<size_t>(kMaxTypedUrlVisits)) { | 441 if (visits.size() > static_cast<size_t>(kMaxTypedUrlVisits)) { |
| 306 int typed_count = 0; | 442 int typed_count = 0; |
| 307 int total = 0; | 443 int total = 0; |
| 308 // Walk the passed-in visit vector and count the # of typed visits. | 444 // Walk the passed-in visit vector and count the # of typed visits. |
| 309 for (VisitRow visit : visits) { | 445 for (VisitRow visit : visits) { |
| 310 // We ignore reload visits. | 446 // We ignore reload visits. |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 361 CHECK_GT(typed_url->visits_size(), 0); | 497 CHECK_GT(typed_url->visits_size(), 0); |
| 362 CHECK_LE(typed_url->visits_size(), kMaxTypedUrlVisits); | 498 CHECK_LE(typed_url->visits_size(), kMaxTypedUrlVisits); |
| 363 CHECK_EQ(typed_url->visits_size(), typed_url->visit_transitions_size()); | 499 CHECK_EQ(typed_url->visits_size(), typed_url->visit_transitions_size()); |
| 364 | 500 |
| 365 return true; | 501 return true; |
| 366 } | 502 } |
| 367 | 503 |
| 368 // static | 504 // static |
| 369 TypedURLSyncBridge::MergeResult TypedURLSyncBridge::MergeUrls( | 505 TypedURLSyncBridge::MergeResult TypedURLSyncBridge::MergeUrls( |
| 370 const TypedUrlSpecifics& sync_url, | 506 const TypedUrlSpecifics& sync_url, |
| 371 const history::URLRow& url, | 507 const URLRow& url, |
| 372 history::VisitVector* visits, | 508 VisitVector* visits, |
| 373 history::URLRow* new_url, | 509 URLRow* new_url, |
| 374 std::vector<history::VisitInfo>* new_visits) { | 510 std::vector<VisitInfo>* new_visits) { |
| 375 DCHECK(new_url); | 511 DCHECK(new_url); |
| 376 DCHECK_EQ(sync_url.url(), url.url().spec()); | 512 DCHECK_EQ(sync_url.url(), url.url().spec()); |
| 377 DCHECK_EQ(sync_url.url(), new_url->url().spec()); | 513 DCHECK_EQ(sync_url.url(), new_url->url().spec()); |
| 378 DCHECK(visits->size()); | 514 DCHECK(visits->size()); |
| 379 DCHECK_GT(sync_url.visits_size(), 0); | 515 DCHECK_GT(sync_url.visits_size(), 0); |
| 380 CHECK_EQ(sync_url.visits_size(), sync_url.visit_transitions_size()); | 516 CHECK_EQ(sync_url.visits_size(), sync_url.visit_transitions_size()); |
| 381 | 517 |
| 382 // Convert these values only once. | 518 // Convert these values only once. |
| 383 base::string16 sync_url_title(base::UTF8ToUTF16(sync_url.title())); | 519 base::string16 sync_url_title(base::UTF8ToUTF16(sync_url.title())); |
| 384 base::Time sync_url_last_visit = base::Time::FromInternalValue( | 520 base::Time sync_url_last_visit = base::Time::FromInternalValue( |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 438 // If the sync_url visit is older than any existing visit in the history | 574 // If the sync_url visit is older than any existing visit in the history |
| 439 // DB, don't re-add it - this keeps us from resurrecting visits that were | 575 // DB, don't re-add it - this keeps us from resurrecting visits that were |
| 440 // aged out locally. | 576 // aged out locally. |
| 441 // | 577 // |
| 442 // TODO(sync): This extra check should be unnecessary now that filtering | 578 // TODO(sync): This extra check should be unnecessary now that filtering |
| 443 // expired visits is performed separately. Non-expired visits older than | 579 // expired visits is performed separately. Non-expired visits older than |
| 444 // the earliest existing history visits should still be synced, so this | 580 // the earliest existing history visits should still be synced, so this |
| 445 // check should be removed. | 581 // check should be removed. |
| 446 if (sync_url_time > earliest_history_time) { | 582 if (sync_url_time > earliest_history_time) { |
| 447 different |= DIFF_LOCAL_VISITS_ADDED; | 583 different |= DIFF_LOCAL_VISITS_ADDED; |
| 448 new_visits->push_back(history::VisitInfo( | 584 new_visits->push_back(VisitInfo( |
| 449 sync_url_time, ui::PageTransitionFromInt(sync_url.visit_transitions( | 585 sync_url_time, ui::PageTransitionFromInt(sync_url.visit_transitions( |
| 450 sync_url_visit_index)))); | 586 sync_url_visit_index)))); |
| 451 } | 587 } |
| 452 // This visit is added to visits below. | 588 // This visit is added to visits below. |
| 453 ++sync_url_visit_index; | 589 ++sync_url_visit_index; |
| 454 } else { | 590 } else { |
| 455 // Same (already synced) entry found in both DBs - no need to do anything. | 591 // Same (already synced) entry found in both DBs - no need to do anything. |
| 456 ++sync_url_visit_index; | 592 ++sync_url_visit_index; |
| 457 ++history_visit_index; | 593 ++history_visit_index; |
| 458 } | 594 } |
| 459 } | 595 } |
| 460 | 596 |
| 461 DCHECK(CheckVisitOrdering(*visits)); | 597 DCHECK(CheckVisitOrdering(*visits)); |
| 462 if (different & DIFF_LOCAL_VISITS_ADDED) { | 598 if (different & DIFF_LOCAL_VISITS_ADDED) { |
| 463 // If the server does not have the same visits as the local db, then the | 599 // If the server does not have the same visits as the local db, then the |
| 464 // new visits from the server need to be added to the vector containing | 600 // new visits from the server need to be added to the vector containing |
| 465 // local visits. These visits will be passed to the server. | 601 // local visits. These visits will be passed to the server. |
| 466 // Insert new visits into the appropriate place in the visits vector. | 602 // Insert new visits into the appropriate place in the visits vector. |
| 467 history::VisitVector::iterator visit_ix = visits->begin(); | 603 VisitVector::iterator visit_ix = visits->begin(); |
| 468 for (std::vector<history::VisitInfo>::iterator new_visit = | 604 for (std::vector<VisitInfo>::iterator new_visit = new_visits->begin(); |
| 469 new_visits->begin(); | |
| 470 new_visit != new_visits->end(); ++new_visit) { | 605 new_visit != new_visits->end(); ++new_visit) { |
| 471 while (visit_ix != visits->end() && | 606 while (visit_ix != visits->end() && |
| 472 new_visit->first > visit_ix->visit_time) { | 607 new_visit->first > visit_ix->visit_time) { |
| 473 ++visit_ix; | 608 ++visit_ix; |
| 474 } | 609 } |
| 475 visit_ix = | 610 visit_ix = visits->insert(visit_ix, VisitRow(url.id(), new_visit->first, |
| 476 visits->insert(visit_ix, history::VisitRow(url.id(), new_visit->first, | 611 0, new_visit->second, 0)); |
| 477 0, new_visit->second, 0)); | |
| 478 ++visit_ix; | 612 ++visit_ix; |
| 479 } | 613 } |
| 480 } | 614 } |
| 481 DCHECK(CheckVisitOrdering(*visits)); | 615 DCHECK(CheckVisitOrdering(*visits)); |
| 482 | 616 |
| 483 new_url->set_last_visit(visits->back().visit_time); | 617 new_url->set_last_visit(visits->back().visit_time); |
| 484 return different; | 618 return different; |
| 485 } | 619 } |
| 486 | 620 |
| 487 // static | 621 // static |
| 622 void TypedURLSyncBridge::DiffVisits( | |
| 623 const VisitVector& history_visits, | |
| 624 const sync_pb::TypedUrlSpecifics& sync_specifics, | |
| 625 std::vector<VisitInfo>* new_visits, | |
| 626 VisitVector* removed_visits) { | |
| 627 DCHECK(new_visits); | |
| 628 size_t old_visit_count = history_visits.size(); | |
| 629 size_t new_visit_count = sync_specifics.visits_size(); | |
| 630 size_t old_index = 0; | |
| 631 size_t new_index = 0; | |
| 632 while (old_index < old_visit_count && new_index < new_visit_count) { | |
| 633 base::Time new_visit_time = | |
| 634 base::Time::FromInternalValue(sync_specifics.visits(new_index)); | |
| 635 if (history_visits[old_index].visit_time < new_visit_time) { | |
| 636 if (new_index > 0 && removed_visits) { | |
| 637 // If there are visits missing from the start of the node, that | |
| 638 // means that they were probably clipped off due to our code that | |
| 639 // limits the size of the sync nodes - don't delete them from our | |
| 640 // local history. | |
| 641 removed_visits->push_back(history_visits[old_index]); | |
| 642 } | |
| 643 ++old_index; | |
| 644 } else if (history_visits[old_index].visit_time > new_visit_time) { | |
| 645 new_visits->push_back(VisitInfo( | |
| 646 new_visit_time, ui::PageTransitionFromInt( | |
| 647 sync_specifics.visit_transitions(new_index)))); | |
| 648 ++new_index; | |
| 649 } else { | |
| 650 ++old_index; | |
| 651 ++new_index; | |
| 652 } | |
| 653 } | |
| 654 | |
| 655 if (removed_visits) { | |
| 656 for (; old_index < old_visit_count; ++old_index) { | |
| 657 removed_visits->push_back(history_visits[old_index]); | |
| 658 } | |
| 659 } | |
| 660 | |
| 661 for (; new_index < new_visit_count; ++new_index) { | |
| 662 new_visits->push_back(VisitInfo( | |
| 663 base::Time::FromInternalValue(sync_specifics.visits(new_index)), | |
| 664 ui::PageTransitionFromInt( | |
| 665 sync_specifics.visit_transitions(new_index)))); | |
| 666 } | |
| 667 } | |
| 668 | |
| 669 // static | |
| 488 void TypedURLSyncBridge::UpdateURLRowFromTypedUrlSpecifics( | 670 void TypedURLSyncBridge::UpdateURLRowFromTypedUrlSpecifics( |
| 489 const TypedUrlSpecifics& typed_url, | 671 const TypedUrlSpecifics& typed_url, |
| 490 history::URLRow* new_url) { | 672 URLRow* new_url) { |
| 491 DCHECK_GT(typed_url.visits_size(), 0); | 673 DCHECK_GT(typed_url.visits_size(), 0); |
| 492 CHECK_EQ(typed_url.visit_transitions_size(), typed_url.visits_size()); | 674 CHECK_EQ(typed_url.visit_transitions_size(), typed_url.visits_size()); |
| 493 if (!new_url->url().is_valid()) { | 675 if (!new_url->url().is_valid()) { |
| 494 new_url->set_url(GURL(typed_url.url())); | 676 new_url->set_url(GURL(typed_url.url())); |
| 495 } | 677 } |
| 496 new_url->set_title(base::UTF8ToUTF16(typed_url.title())); | 678 new_url->set_title(base::UTF8ToUTF16(typed_url.title())); |
| 497 new_url->set_hidden(typed_url.hidden()); | 679 new_url->set_hidden(typed_url.hidden()); |
| 498 // Only provide the initial value for the last_visit field - after that, let | 680 // Only provide the initial value for the last_visit field - after that, let |
| 499 // the history code update the last_visit field on its own. | 681 // the history code update the last_visit field on its own. |
| 500 if (new_url->last_visit().is_null()) { | 682 if (new_url->last_visit().is_null()) { |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 518 return; | 700 return; |
| 519 } | 701 } |
| 520 change_processor()->ModelReadyToSync(std::move(batch)); | 702 change_processor()->ModelReadyToSync(std::move(batch)); |
| 521 } | 703 } |
| 522 | 704 |
| 523 void TypedURLSyncBridge::ClearErrorStats() { | 705 void TypedURLSyncBridge::ClearErrorStats() { |
| 524 num_db_accesses_ = 0; | 706 num_db_accesses_ = 0; |
| 525 num_db_errors_ = 0; | 707 num_db_errors_ = 0; |
| 526 } | 708 } |
| 527 | 709 |
| 528 void TypedURLSyncBridge::UpdateUrlFromServer( | 710 void TypedURLSyncBridge::MergeURLWithSync( |
| 529 const sync_pb::TypedUrlSpecifics& server_typed_url, | 711 const sync_pb::TypedUrlSpecifics& server_typed_url, |
| 530 TypedURLMap* local_typed_urls, | 712 TypedURLMap* local_typed_urls, |
| 531 URLVisitVectorMap* local_visit_vectors, | 713 URLVisitVectorMap* local_visit_vectors, |
| 532 history::URLRows* new_synced_urls, | 714 URLRows* new_synced_urls, |
| 533 TypedURLVisitVector* new_synced_visits, | 715 TypedURLVisitVector* new_synced_visits, |
| 534 history::URLRows* updated_synced_urls) { | 716 URLRows* updated_synced_urls) { |
| 535 DCHECK(server_typed_url.visits_size() != 0); | 717 DCHECK(server_typed_url.visits_size() != 0); |
| 536 DCHECK_EQ(server_typed_url.visits_size(), | 718 DCHECK_EQ(server_typed_url.visits_size(), |
| 537 server_typed_url.visit_transitions_size()); | 719 server_typed_url.visit_transitions_size()); |
| 538 | 720 |
| 539 // Ignore empty urls. | 721 // Ignore empty urls. |
| 540 if (server_typed_url.url().empty()) { | 722 if (server_typed_url.url().empty()) { |
| 541 DVLOG(1) << "Ignoring empty URL in sync DB"; | 723 DVLOG(1) << "Ignoring empty URL in sync DB"; |
| 542 return; | 724 return; |
| 543 } | 725 } |
| 544 // Now, get rid of the expired visits. If there are no un-expired visits | 726 // Now, get rid of the expired visits. If there are no un-expired visits |
| 545 // left, ignore this url - any local data should just replace it. | 727 // left, ignore this url - any local data should just replace it. |
| 546 TypedUrlSpecifics sync_url = FilterExpiredVisits(server_typed_url); | 728 TypedUrlSpecifics sync_url = FilterExpiredVisits(server_typed_url); |
| 547 if (sync_url.visits_size() == 0) { | 729 if (sync_url.visits_size() == 0) { |
| 548 DVLOG(1) << "Ignoring expired URL in sync DB: " << sync_url.url(); | 730 DVLOG(1) << "Ignoring expired URL in sync DB: " << sync_url.url(); |
| 549 return; | 731 return; |
| 550 } | 732 } |
| 551 | 733 |
| 552 // Check if local db already has the url from sync. | 734 // Check if local db already has the url from sync. |
| 553 TypedURLMap::iterator it = local_typed_urls->find(GURL(sync_url.url())); | 735 TypedURLMap::iterator it = local_typed_urls->find(GURL(sync_url.url())); |
| 554 if (it == local_typed_urls->end()) { | 736 if (it == local_typed_urls->end()) { |
| 555 // There are no matching typed urls from the local db, check for untyped | 737 // There are no matching typed urls from the local db, check for untyped |
| 556 history::URLRow untyped_url(GURL(sync_url.url())); | 738 URLRow untyped_url(GURL(sync_url.url())); |
| 557 | 739 |
| 558 // The URL may still exist in the local db if it is an untyped url. | 740 // The URL may still exist in the local db if it is an untyped url. |
| 559 // An untyped url will transition to a typed url after receiving visits | 741 // An untyped url will transition to a typed url after receiving visits |
| 560 // from sync, and sync should receive any visits already existing locally | 742 // from sync, and sync should receive any visits already existing locally |
| 561 // for the url, so the full list of visits is consistent. | 743 // for the url, so the full list of visits is consistent. |
| 562 bool is_existing_url = | 744 bool is_existing_url = |
| 563 history_backend_->GetURL(untyped_url.url(), &untyped_url); | 745 history_backend_->GetURL(untyped_url.url(), &untyped_url); |
| 564 if (is_existing_url) { | 746 if (is_existing_url) { |
| 565 // Add a new entry to |local_typed_urls|, and set the iterator to it. | 747 // Add a new entry to |local_typed_urls|, and set the iterator to it. |
| 566 history::VisitVector untyped_visits; | 748 VisitVector untyped_visits; |
| 567 if (!FixupURLAndGetVisits(&untyped_url, &untyped_visits)) { | 749 if (!FixupURLAndGetVisits(&untyped_url, &untyped_visits)) { |
| 568 return; | 750 return; |
| 569 } | 751 } |
| 570 (*local_visit_vectors)[untyped_url.url()] = untyped_visits; | 752 (*local_visit_vectors)[untyped_url.url()] = untyped_visits; |
| 571 | 753 |
| 572 // Store row info that will be used to update sync's visits. | 754 // Store row info that will be used to update sync's visits. |
| 573 (*local_typed_urls)[untyped_url.url()] = untyped_url; | 755 (*local_typed_urls)[untyped_url.url()] = untyped_url; |
| 574 | 756 |
| 575 // Set iterator |it| to point to this entry. | 757 // Set iterator |it| to point to this entry. |
| 576 it = local_typed_urls->find(untyped_url.url()); | 758 it = local_typed_urls->find(untyped_url.url()); |
| 577 DCHECK(it != local_typed_urls->end()); | 759 DCHECK(it != local_typed_urls->end()); |
| 578 // Continue with merge below. | 760 // Continue with merge below. |
| 579 } else { | 761 } else { |
| 580 // The url is new to the local history DB. | 762 // The url is new to the local history DB. |
| 581 // Create new db entry for url. | 763 // Create new db entry for url. |
| 582 history::URLRow new_url(GURL(sync_url.url())); | 764 URLRow new_url(GURL(sync_url.url())); |
| 583 UpdateURLRowFromTypedUrlSpecifics(sync_url, &new_url); | 765 UpdateURLRowFromTypedUrlSpecifics(sync_url, &new_url); |
| 584 new_synced_urls->push_back(new_url); | 766 new_synced_urls->push_back(new_url); |
| 585 | 767 |
| 586 // Add entries for url visits. | 768 // Add entries for url visits. |
| 587 std::vector<history::VisitInfo> added_visits; | 769 std::vector<VisitInfo> added_visits; |
| 588 size_t visit_count = sync_url.visits_size(); | 770 size_t visit_count = sync_url.visits_size(); |
| 589 | 771 |
| 590 for (size_t index = 0; index < visit_count; ++index) { | 772 for (size_t index = 0; index < visit_count; ++index) { |
| 591 base::Time visit_time = | 773 base::Time visit_time = |
| 592 base::Time::FromInternalValue(sync_url.visits(index)); | 774 base::Time::FromInternalValue(sync_url.visits(index)); |
| 593 ui::PageTransition transition = | 775 ui::PageTransition transition = |
| 594 ui::PageTransitionFromInt(sync_url.visit_transitions(index)); | 776 ui::PageTransitionFromInt(sync_url.visit_transitions(index)); |
| 595 added_visits.push_back(history::VisitInfo(visit_time, transition)); | 777 added_visits.push_back(VisitInfo(visit_time, transition)); |
| 596 } | 778 } |
| 597 new_synced_visits->push_back( | 779 new_synced_visits->push_back( |
| 598 std::pair<GURL, std::vector<history::VisitInfo>>(new_url.url(), | 780 std::pair<GURL, std::vector<VisitInfo>>(new_url.url(), added_visits)); |
| 599 added_visits)); | |
| 600 return; | 781 return; |
| 601 } | 782 } |
| 602 } | 783 } |
| 603 | 784 |
| 604 // Same URL exists in sync data and in history data - compare the | 785 // Same URL exists in sync data and in history data - compare the |
| 605 // entries to see if there's any difference. | 786 // entries to see if there's any difference. |
| 606 history::VisitVector& visits = (*local_visit_vectors)[it->first]; | 787 VisitVector& visits = (*local_visit_vectors)[it->first]; |
| 607 std::vector<history::VisitInfo> added_visits; | 788 std::vector<VisitInfo> added_visits; |
| 608 | 789 |
| 609 // Empty URLs should be filtered out by ShouldIgnoreUrl() previously. | 790 // Empty URLs should be filtered out by ShouldIgnoreUrl() previously. |
| 610 DCHECK(!it->second.url().spec().empty()); | 791 DCHECK(!it->second.url().spec().empty()); |
| 611 | 792 |
| 612 // Initialize fields in |new_url| to the same values as the fields in | 793 // Initialize fields in |new_url| to the same values as the fields in |
| 613 // the existing URLRow in the history DB. This is needed because we | 794 // the existing URLRow in the history DB. This is needed because we |
| 614 // overwrite the existing value in WriteToHistoryBackend(), but some of | 795 // overwrite the existing value in WriteToHistoryBackend(), but some of |
| 615 // the values in that structure are not synced (like typed_count). | 796 // the values in that structure are not synced (like typed_count). |
| 616 history::URLRow new_url(it->second); | 797 URLRow new_url(it->second); |
| 617 | 798 |
| 618 MergeResult difference = | 799 MergeResult difference = |
| 619 MergeUrls(sync_url, it->second, &visits, &new_url, &added_visits); | 800 MergeUrls(sync_url, it->second, &visits, &new_url, &added_visits); |
| 620 | 801 |
| 621 if (difference != DIFF_NONE) { | 802 if (difference != DIFF_NONE) { |
| 622 it->second = new_url; | 803 it->second = new_url; |
| 623 if (difference & DIFF_UPDATE_NODE) { | 804 if (difference & DIFF_UPDATE_NODE) { |
| 624 // We don't want to resurrect old visits that have been aged out by | 805 // We don't want to resurrect old visits that have been aged out by |
| 625 // other clients, so remove all visits that are older than the | 806 // other clients, so remove all visits that are older than the |
| 626 // earliest existing visit in the sync node. | 807 // earliest existing visit in the sync node. |
| 627 // | 808 // |
| 628 // TODO(sync): This logic should be unnecessary now that filtering of | 809 // TODO(sync): This logic should be unnecessary now that filtering of |
| 629 // expired visits is performed separately. Non-expired visits older than | 810 // expired visits is performed separately. Non-expired visits older than |
| 630 // the earliest existing sync visits should still be synced, so this | 811 // the earliest existing sync visits should still be synced, so this |
| 631 // logic should be removed. | 812 // logic should be removed. |
| 632 if (sync_url.visits_size() > 0) { | 813 if (sync_url.visits_size() > 0) { |
| 633 base::Time earliest_visit = | 814 base::Time earliest_visit = |
| 634 base::Time::FromInternalValue(sync_url.visits(0)); | 815 base::Time::FromInternalValue(sync_url.visits(0)); |
| 635 for (history::VisitVector::iterator i = visits.begin(); | 816 for (VisitVector::iterator i = visits.begin(); |
| 636 i != visits.end() && i->visit_time < earliest_visit;) { | 817 i != visits.end() && i->visit_time < earliest_visit;) { |
| 637 i = visits.erase(i); | 818 i = visits.erase(i); |
| 638 } | 819 } |
| 639 // Should never be possible to delete all the items, since the | 820 // Should never be possible to delete all the items, since the |
| 640 // visit vector contains newer local visits it will keep and/or the | 821 // visit vector contains newer local visits it will keep and/or the |
| 641 // visits in typed_url.visits newer than older local visits. | 822 // visits in typed_url.visits newer than older local visits. |
| 642 DCHECK(visits.size() > 0); | 823 DCHECK(visits.size() > 0); |
| 643 } | 824 } |
| 644 DCHECK_EQ(new_url.last_visit().ToInternalValue(), | 825 DCHECK_EQ(new_url.last_visit().ToInternalValue(), |
| 645 visits.back().visit_time.ToInternalValue()); | 826 visits.back().visit_time.ToInternalValue()); |
| 646 } | 827 } |
| 647 if (difference & DIFF_LOCAL_ROW_CHANGED) { | 828 if (difference & DIFF_LOCAL_ROW_CHANGED) { |
| 648 // Add entry to updated_synced_urls to update the local db. | 829 // Add entry to updated_synced_urls to update the local db. |
| 649 DCHECK_EQ(it->second.id(), new_url.id()); | 830 DCHECK_EQ(it->second.id(), new_url.id()); |
| 650 updated_synced_urls->push_back(new_url); | 831 updated_synced_urls->push_back(new_url); |
| 651 } | 832 } |
| 652 if (difference & DIFF_LOCAL_VISITS_ADDED) { | 833 if (difference & DIFF_LOCAL_VISITS_ADDED) { |
| 653 // Add entry with new visits to new_synced_visits to update the local db. | 834 // Add entry with new visits to new_synced_visits to update the local db. |
| 654 new_synced_visits->push_back( | 835 new_synced_visits->push_back( |
| 655 std::pair<GURL, std::vector<history::VisitInfo>>(it->first, | 836 std::pair<GURL, std::vector<VisitInfo>>(it->first, added_visits)); |
| 656 added_visits)); | |
| 657 } | 837 } |
| 658 } else { | 838 } else { |
| 659 // No difference in urls, erase from map | 839 // No difference in urls, erase from map |
| 660 local_typed_urls->erase(it); | 840 local_typed_urls->erase(it); |
| 661 } | 841 } |
| 662 } | 842 } |
| 663 | 843 |
| 844 void TypedURLSyncBridge::UpdateFromSync( | |
| 845 const sync_pb::TypedUrlSpecifics& typed_url, | |
| 846 TypedURLVisitVector* visits_to_add, | |
| 847 VisitVector* visits_to_remove, | |
| 848 URLRows* updated_urls, | |
| 849 URLRows* new_urls) { | |
| 850 URLRow new_url(GURL(typed_url.url())); | |
| 851 VisitVector existing_visits; | |
| 852 bool existing_url = history_backend_->GetURL(new_url.url(), &new_url); | |
| 853 if (existing_url) { | |
| 854 // This URL already exists locally - fetch the visits so we can | |
| 855 // merge them below. | |
| 856 if (!FixupURLAndGetVisits(&new_url, &existing_visits)) { | |
| 857 return; | |
| 858 } | |
| 859 } | |
| 860 visits_to_add->push_back(std::pair<GURL, std::vector<VisitInfo>>( | |
| 861 new_url.url(), std::vector<VisitInfo>())); | |
| 862 | |
| 863 // Update the URL with information from the typed URL. | |
| 864 UpdateURLRowFromTypedUrlSpecifics(typed_url, &new_url); | |
| 865 | |
| 866 // Figure out which visits we need to add. | |
| 867 DiffVisits(existing_visits, typed_url, &visits_to_add->back().second, | |
| 868 visits_to_remove); | |
| 869 | |
| 870 if (existing_url) { | |
| 871 updated_urls->push_back(new_url); | |
| 872 } else { | |
| 873 new_urls->push_back(new_url); | |
| 874 } | |
| 875 } | |
| 876 | |
| 877 void TypedURLSyncBridge::UpdateSyncFromLocal( | |
| 878 URLRow row, | |
| 879 MetadataChangeList* metadata_change_list) { | |
| 880 DCHECK_GE(row.typed_count(), 0); | |
| 881 | |
| 882 if (ShouldIgnoreUrl(row.url())) | |
| 883 return; | |
| 884 | |
| 885 // Get the visits for this node. | |
| 886 VisitVector visit_vector; | |
| 887 if (!FixupURLAndGetVisits(&row, &visit_vector)) { | |
| 888 return; | |
| 889 } | |
| 890 | |
| 891 SendTypedURLToProcessor(row, visit_vector, metadata_change_list); | |
| 892 | |
| 893 return; | |
| 894 } | |
| 895 | |
| 664 base::Optional<ModelError> TypedURLSyncBridge::WriteToHistoryBackend( | 896 base::Optional<ModelError> TypedURLSyncBridge::WriteToHistoryBackend( |
| 665 const history::URLRows* new_urls, | 897 const URLRows* new_urls, |
| 666 const history::URLRows* updated_urls, | 898 const URLRows* updated_urls, |
| 667 const std::vector<GURL>* deleted_urls, | 899 const std::vector<GURL>* deleted_urls, |
| 668 const TypedURLVisitVector* new_visits, | 900 const TypedURLVisitVector* new_visits, |
| 669 const history::VisitVector* deleted_visits) { | 901 const VisitVector* deleted_visits) { |
| 670 if (deleted_urls && !deleted_urls->empty()) | 902 if (deleted_urls && !deleted_urls->empty()) |
| 671 history_backend_->DeleteURLs(*deleted_urls); | 903 history_backend_->DeleteURLs(*deleted_urls); |
| 672 | 904 |
| 673 if (new_urls) { | 905 if (new_urls) { |
| 674 history_backend_->AddPagesWithDetails(*new_urls, history::SOURCE_SYNCED); | 906 history_backend_->AddPagesWithDetails(*new_urls, SOURCE_SYNCED); |
| 675 } | 907 } |
| 676 | 908 |
| 677 if (updated_urls) { | 909 if (updated_urls) { |
| 678 ++num_db_accesses_; | 910 ++num_db_accesses_; |
| 679 // This is an existing entry in the URL database. We don't verify the | 911 // This is an existing entry in the URL database. We don't verify the |
| 680 // visit_count or typed_count values here, because either one (or both) | 912 // visit_count or typed_count values here, because either one (or both) |
| 681 // could be zero in the case of bookmarks, or in the case of a URL | 913 // could be zero in the case of bookmarks, or in the case of a URL |
| 682 // transitioning from non-typed to typed as a result of this sync. | 914 // transitioning from non-typed to typed as a result of this sync. |
| 683 // In the field we sometimes run into errors on specific URLs. It's OK | 915 // In the field we sometimes run into errors on specific URLs. It's OK |
| 684 // to just continue on (we can try writing again on the next model | 916 // to just continue on (we can try writing again on the next model |
| 685 // association). | 917 // association). |
| 686 size_t num_successful_updates = history_backend_->UpdateURLs(*updated_urls); | 918 size_t num_successful_updates = history_backend_->UpdateURLs(*updated_urls); |
| 687 num_db_errors_ += updated_urls->size() - num_successful_updates; | 919 num_db_errors_ += updated_urls->size() - num_successful_updates; |
| 688 } | 920 } |
| 689 | 921 |
| 690 if (new_visits) { | 922 if (new_visits) { |
| 691 for (const auto& visits : *new_visits) { | 923 for (const auto& visits : *new_visits) { |
| 692 // If there are no visits to add, just skip this. | 924 // If there are no visits to add, just skip this. |
| 693 if (visits.second.empty()) | 925 if (visits.second.empty()) |
| 694 continue; | 926 continue; |
| 695 ++num_db_accesses_; | 927 ++num_db_accesses_; |
| 696 if (!history_backend_->AddVisits(visits.first, visits.second, | 928 if (!history_backend_->AddVisits(visits.first, visits.second, |
| 697 history::SOURCE_SYNCED)) { | 929 SOURCE_SYNCED)) { |
| 698 ++num_db_errors_; | 930 ++num_db_errors_; |
| 699 return ModelError(FROM_HERE, "Could not add visits to HistoryBackend."); | 931 return ModelError(FROM_HERE, "Could not add visits to HistoryBackend."); |
| 700 } | 932 } |
| 701 } | 933 } |
| 702 } | 934 } |
| 703 | 935 |
| 704 if (deleted_visits) { | 936 if (deleted_visits) { |
| 705 ++num_db_accesses_; | 937 ++num_db_accesses_; |
| 706 if (!history_backend_->RemoveVisits(*deleted_visits)) { | 938 if (!history_backend_->RemoveVisits(*deleted_visits)) { |
| 707 ++num_db_errors_; | 939 ++num_db_errors_; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 741 return true; | 973 return true; |
| 742 | 974 |
| 743 // Ignore local file URLs. | 975 // Ignore local file URLs. |
| 744 if (url.SchemeIsFile()) | 976 if (url.SchemeIsFile()) |
| 745 return true; | 977 return true; |
| 746 | 978 |
| 747 // Ignore localhost URLs. | 979 // Ignore localhost URLs. |
| 748 if (net::IsLocalhost(url.host_piece())) | 980 if (net::IsLocalhost(url.host_piece())) |
| 749 return true; | 981 return true; |
| 750 | 982 |
| 751 // Ignore username and password, sonce history backend will remove user name | 983 // Ignore username and password, since history backend will remove user name |
| 752 // and password in URLDatabase::GURLToDatabaseURL and send username/password | 984 // and password in URLDatabase::GURLToDatabaseURL and send username/password |
| 753 // removed url to sync later. | 985 // removed url to sync later. |
| 754 if (url.has_username() || url.has_password()) | 986 if (url.has_username() || url.has_password()) |
| 755 return true; | 987 return true; |
| 756 | 988 |
| 757 return false; | 989 return false; |
| 758 } | 990 } |
| 759 | 991 |
| 760 bool TypedURLSyncBridge::ShouldIgnoreVisits( | 992 bool TypedURLSyncBridge::ShouldIgnoreVisits(const VisitVector& visits) { |
| 761 const history::VisitVector& visits) { | |
| 762 // We ignore URLs that were imported, but have never been visited by | 993 // We ignore URLs that were imported, but have never been visited by |
| 763 // chromium. | 994 // chromium. |
| 764 static const int kFirstImportedSource = history::SOURCE_FIREFOX_IMPORTED; | 995 static const int kFirstImportedSource = SOURCE_FIREFOX_IMPORTED; |
| 765 history::VisitSourceMap map; | 996 VisitSourceMap map; |
| 766 if (!history_backend_->GetVisitsSource(visits, &map)) | 997 if (!history_backend_->GetVisitsSource(visits, &map)) |
| 767 return false; // If we can't read the visit, assume it's not imported. | 998 return false; // If we can't read the visit, assume it's not imported. |
| 768 | 999 |
| 769 // Walk the list of visits and look for a non-imported item. | 1000 // Walk the list of visits and look for a non-imported item. |
| 770 for (const auto& visit : visits) { | 1001 for (const auto& visit : visits) { |
| 771 if (map.count(visit.visit_id) == 0 || | 1002 if (map.count(visit.visit_id) == 0 || |
| 772 map[visit.visit_id] < kFirstImportedSource) { | 1003 map[visit.visit_id] < kFirstImportedSource) { |
| 773 return false; | 1004 return false; |
| 774 } | 1005 } |
| 775 } | 1006 } |
| 776 // We only saw imported visits, so tell the caller to ignore them. | 1007 // We only saw imported visits, so tell the caller to ignore them. |
| 777 return true; | 1008 return true; |
| 778 } | 1009 } |
| 779 | 1010 |
| 1011 bool TypedURLSyncBridge::ShouldSyncVisit(int typed_count, | |
| 1012 ui::PageTransition transition) { | |
| 1013 // Just use an ad-hoc criteria to determine whether to ignore this | |
| 1014 // notification. For most users, the distribution of visits is roughly a bell | |
| 1015 // curve with a long tail - there are lots of URLs with < 5 visits so we want | |
| 1016 // to make sure we sync up every visit to ensure the proper ordering of | |
| 1017 // suggestions. But there are relatively few URLs with > 10 visits, and those | |
| 1018 // tend to be more broadly distributed such that there's no need to sync up | |
| 1019 // every visit to preserve their relative ordering. | |
| 1020 return (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED) && | |
| 1021 typed_count >= 0 && | |
| 1022 (typed_count < kTypedUrlVisitThrottleThreshold || | |
| 1023 (typed_count % kTypedUrlVisitThrottleMultiple) == 0)); | |
| 1024 } | |
| 1025 | |
| 780 bool TypedURLSyncBridge::FixupURLAndGetVisits(URLRow* url, | 1026 bool TypedURLSyncBridge::FixupURLAndGetVisits(URLRow* url, |
| 781 VisitVector* visits) { | 1027 VisitVector* visits) { |
| 782 ++num_db_accesses_; | 1028 ++num_db_accesses_; |
| 783 if (!history_backend_->GetMostRecentVisitsForURL(url->id(), kMaxVisitsToFetch, | 1029 if (!history_backend_->GetMostRecentVisitsForURL(url->id(), kMaxVisitsToFetch, |
| 784 visits)) { | 1030 visits)) { |
| 785 ++num_db_errors_; | 1031 ++num_db_errors_; |
| 786 // Couldn't load the visits for this URL due to some kind of DB error. | 1032 // Couldn't load the visits for this URL due to some kind of DB error. |
| 787 // Don't bother writing this URL to the history DB (if we ignore the | 1033 // Don't bother writing this URL to the history DB (if we ignore the |
| 788 // error and continue, we might end up duplicating existing visits). | 1034 // error and continue, we might end up duplicating existing visits). |
| 789 DLOG(ERROR) << "Could not load visits for url: " << url->url(); | 1035 DLOG(ERROR) << "Could not load visits for url: " << url->url(); |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 845 } | 1091 } |
| 846 | 1092 |
| 847 std::unique_ptr<EntityData> TypedURLSyncBridge::CreateEntityData( | 1093 std::unique_ptr<EntityData> TypedURLSyncBridge::CreateEntityData( |
| 848 const URLRow& row, | 1094 const URLRow& row, |
| 849 const VisitVector& visits) { | 1095 const VisitVector& visits) { |
| 850 auto entity_data = base::MakeUnique<EntityData>(); | 1096 auto entity_data = base::MakeUnique<EntityData>(); |
| 851 TypedUrlSpecifics* specifics = entity_data->specifics.mutable_typed_url(); | 1097 TypedUrlSpecifics* specifics = entity_data->specifics.mutable_typed_url(); |
| 852 | 1098 |
| 853 if (!WriteToTypedUrlSpecifics(row, visits, specifics)) { | 1099 if (!WriteToTypedUrlSpecifics(row, visits, specifics)) { |
| 854 // Cannot write to specifics, ex. no TYPED visits. | 1100 // Cannot write to specifics, ex. no TYPED visits. |
| 855 return base::MakeUnique<EntityData>(); | 1101 return base::MakeUnique<EntityData>(); |
|
pavely
2017/07/14 19:15:57
One more option would be to return nullptr instead
Gang Wu
2017/07/17 18:59:37
Done.
| |
| 856 } | 1102 } |
| 857 entity_data->non_unique_name = row.url().spec(); | 1103 entity_data->non_unique_name = row.url().spec(); |
| 858 | |
| 859 return entity_data; | 1104 return entity_data; |
| 860 } | 1105 } |
| 861 | 1106 |
| 862 bool TypedURLSyncBridge::GetValidURLsAndVisits(URLVisitVectorMap* url_to_visit, | 1107 bool TypedURLSyncBridge::GetValidURLsAndVisits(URLVisitVectorMap* url_to_visit, |
| 863 TypedURLMap* url_to_urlrow) { | 1108 TypedURLMap* url_to_urlrow) { |
| 864 DCHECK(url_to_visit); | 1109 DCHECK(url_to_visit); |
| 865 DCHECK(url_to_urlrow); | 1110 DCHECK(url_to_urlrow); |
| 866 | 1111 |
| 867 history::URLRows local_typed_urls; | 1112 URLRows local_typed_urls; |
| 868 ++num_db_accesses_; | 1113 ++num_db_accesses_; |
| 869 if (!history_backend_->GetAllTypedURLs(&local_typed_urls)) { | 1114 if (!history_backend_->GetAllTypedURLs(&local_typed_urls)) { |
| 870 ++num_db_errors_; | 1115 ++num_db_errors_; |
| 871 return false; | 1116 return false; |
| 872 } | 1117 } |
| 873 for (history::URLRow& url : local_typed_urls) { | 1118 for (URLRow& url : local_typed_urls) { |
| 874 DCHECK_EQ(0U, url_to_visit->count(url.url())); | 1119 DCHECK_EQ(0U, url_to_visit->count(url.url())); |
| 875 if (!FixupURLAndGetVisits(&url, &((*url_to_visit)[url.url()])) || | 1120 if (!FixupURLAndGetVisits(&url, &((*url_to_visit)[url.url()])) || |
| 876 ShouldIgnoreUrl(url.url()) || | 1121 ShouldIgnoreUrl(url.url()) || |
| 877 ShouldIgnoreVisits((*url_to_visit)[url.url()])) { | 1122 ShouldIgnoreVisits((*url_to_visit)[url.url()])) { |
| 878 // Ignore this URL if we couldn't load the visits or if there's some | 1123 // Ignore this URL if we couldn't load the visits or if there's some |
| 879 // other problem with it (it was empty, or imported and never visited). | 1124 // other problem with it (it was empty, or imported and never visited). |
| 880 } else { | 1125 } else { |
| 881 // Add url to url_to_urlrow. | 1126 // Add url to url_to_urlrow. |
| 882 (*url_to_urlrow)[url.url()] = url; | 1127 (*url_to_urlrow)[url.url()] = url; |
| 883 } | 1128 } |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 894 | 1139 |
| 895 if (!is_existing_url) { | 1140 if (!is_existing_url) { |
| 896 // The typed url did not save to local history database, so return empty | 1141 // The typed url did not save to local history database, so return empty |
| 897 // string. | 1142 // string. |
| 898 return std::string(); | 1143 return std::string(); |
| 899 } | 1144 } |
| 900 | 1145 |
| 901 return GetStorageKeyFromURLRow(existing_url); | 1146 return GetStorageKeyFromURLRow(existing_url); |
| 902 } | 1147 } |
| 903 | 1148 |
| 1149 void TypedURLSyncBridge::SendTypedURLToProcessor( | |
| 1150 const URLRow& row, | |
| 1151 const VisitVector& visits, | |
| 1152 MetadataChangeList* metadata_change_list) { | |
| 1153 DCHECK(!visits.empty()); | |
| 1154 DCHECK(metadata_change_list); | |
| 1155 | |
| 1156 std::unique_ptr<syncer::EntityData> entity_data = | |
| 1157 CreateEntityData(row, visits); | |
| 1158 if (!entity_data->specifics.has_typed_url()) { | |
| 1159 // Cannot create EntityData, ex. no TYPED visits. | |
| 1160 return; | |
| 1161 } | |
| 1162 | |
| 1163 std::string storage_key = GetStorageKeyFromURLRow(row); | |
| 1164 change_processor()->Put(storage_key, std::move(entity_data), | |
| 1165 metadata_change_list); | |
| 1166 } | |
| 1167 | |
| 904 } // namespace history | 1168 } // namespace history |
| OLD | NEW |