Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(130)

Side by Side Diff: components/history/core/browser/typed_url_sync_bridge.cc

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

Powered by Google App Engine
This is Rietveld 408576698