1b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// found in the LICENSE file.
4b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
5b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "chrome/browser/history/typed_url_syncable_service.h"
6b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
7b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "base/auto_reset.h"
8b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "base/logging.h"
9b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "base/metrics/histogram.h"
10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
11b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "chrome/browser/history/history_backend.h"
12b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "net/base/net_util.h"
13b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "sync/protocol/sync.pb.h"
14b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "sync/protocol/typed_url_specifics.pb.h"
15b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
16b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)namespace {
17b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
18b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// The server backend can't handle arbitrarily large node sizes, so to keep
19b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// the size under control we limit the visit array.
20b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)static const int kMaxTypedUrlVisits = 100;
21b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
22b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// There's no limit on how many visits the history DB could have for a given
23b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// typed URL, so we limit how many we fetch from the DB to avoid crashes due to
24b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// running out of memory (http://crbug.com/89793). This value is different
25b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// from kMaxTypedUrlVisits, as some of the visits fetched from the DB may be
26b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// RELOAD visits, which will be stripped.
27b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)static const int kMaxVisitsToFetch = 1000;
28b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
29b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// This is the threshold at which we start throttling sync updates for typed
30b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// URLs - any URLs with a typed_count >= this threshold will be throttled.
31b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)static const int kTypedUrlVisitThrottleThreshold = 10;
32b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
33b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// This is the multiple we use when throttling sync updates. If the multiple is
34b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// N, we sync up every Nth update (i.e. when typed_count % N == 0).
35b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)static const int kTypedUrlVisitThrottleMultiple = 10;
36b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
37b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}  // namespace
38b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
39b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)namespace history {
40b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
41b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char kTypedUrlTag[] = "google_chrome_typed_urls";
42b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
43b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)static bool CheckVisitOrdering(const VisitVector& visits) {
44b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  int64 previous_visit_time = 0;
45b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  for (VisitVector::const_iterator visit = visits.begin();
46b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)       visit != visits.end(); ++visit) {
47b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (visit != visits.begin()) {
48b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      // We allow duplicate visits here - they shouldn't really be allowed, but
49b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      // they still seem to show up sometimes and we haven't figured out the
50b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      // source, so we just log an error instead of failing an assertion.
51b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      // (http://crbug.com/91473).
52b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      if (previous_visit_time == visit->visit_time.ToInternalValue())
53b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        DVLOG(1) << "Duplicate visit time encountered";
54b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      else if (previous_visit_time > visit->visit_time.ToInternalValue())
55b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        return false;
56b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
57b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
58b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    previous_visit_time = visit->visit_time.ToInternalValue();
59b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
60b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return true;
61b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
62b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
63b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)TypedUrlSyncableService::TypedUrlSyncableService(
64b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    HistoryBackend* history_backend)
65b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    : history_backend_(history_backend),
66b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      processing_syncer_changes_(false),
6790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      expected_loop_(base::MessageLoop::current()) {
68b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK(history_backend_);
6990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DCHECK(expected_loop_ == base::MessageLoop::current());
70b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
71b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
72b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)TypedUrlSyncableService::~TypedUrlSyncableService() {
7390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DCHECK(expected_loop_ == base::MessageLoop::current());
74b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
75b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
76b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)syncer::SyncMergeResult TypedUrlSyncableService::MergeDataAndStartSyncing(
77b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    syncer::ModelType type,
78b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const syncer::SyncDataList& initial_sync_data,
79b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
80b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    scoped_ptr<syncer::SyncErrorFactory> error_handler) {
8190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DCHECK(expected_loop_ == base::MessageLoop::current());
82b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK(!sync_processor_.get());
83b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK(sync_processor.get());
84b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK(error_handler.get());
85b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK_EQ(type, syncer::TYPED_URLS);
86b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
87b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  syncer::SyncMergeResult merge_result(type);
88b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  sync_processor_ = sync_processor.Pass();
89b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  sync_error_handler_ = error_handler.Pass();
90b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
91b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // TODO(mgist): Add implementation
92b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
93b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return merge_result;
94b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
95b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
96b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void TypedUrlSyncableService::StopSyncing(syncer::ModelType type) {
9790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DCHECK(expected_loop_ == base::MessageLoop::current());
98b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK_EQ(type, syncer::TYPED_URLS);
99b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
100b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  sync_processor_.reset();
101b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  sync_error_handler_.reset();
102b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
103b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
104b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)syncer::SyncDataList TypedUrlSyncableService::GetAllSyncData(
105b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    syncer::ModelType type) const {
10690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DCHECK(expected_loop_ == base::MessageLoop::current());
107b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  syncer::SyncDataList list;
108b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
109b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // TODO(mgist): Add implementation
110b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
111b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return list;
112b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
113b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
114b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)syncer::SyncError TypedUrlSyncableService::ProcessSyncChanges(
115b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const tracked_objects::Location& from_here,
116b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const syncer::SyncChangeList& change_list) {
11790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DCHECK(expected_loop_ == base::MessageLoop::current());
118b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
119b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // TODO(mgist): Add implementation
120b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
121b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return syncer::SyncError(FROM_HERE,
122eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                           syncer::SyncError::DATATYPE_ERROR,
123b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                           "Typed url syncable service is not implemented.",
124b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                           syncer::TYPED_URLS);
125b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
126b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
127b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void TypedUrlSyncableService::OnUrlsModified(URLRows* changed_urls) {
12890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DCHECK(expected_loop_ == base::MessageLoop::current());
129b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK(changed_urls);
130b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
131b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (processing_syncer_changes_)
132b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return;  // These are changes originating from us, ignore.
133b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (!sync_processor_.get())
134b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return;  // Sync processor not yet initialized, don't sync.
135b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
136b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Create SyncChangeList.
137b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  syncer::SyncChangeList changes;
138b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
139b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  for (URLRows::iterator url = changed_urls->begin();
140b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)       url != changed_urls->end(); ++url) {
141b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // Only care if the modified URL is typed.
142b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (url->typed_count() > 0) {
143b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      // If there were any errors updating the sync node, just ignore them and
144b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      // continue on to process the next URL.
145b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      CreateOrUpdateSyncNode(*url, &changes);
146b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
147b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
148b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
149b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Send SyncChangeList to server if there are any changes.
150b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (changes.size() > 0)
151b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
152b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
153b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
154b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void TypedUrlSyncableService::OnUrlVisited(content::PageTransition transition,
155b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                                           URLRow* row) {
15690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DCHECK(expected_loop_ == base::MessageLoop::current());
157b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK(row);
158b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
159b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (processing_syncer_changes_)
160b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return;  // These are changes originating from us, ignore.
161b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (!sync_processor_.get())
162b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return;  // Sync processor not yet initialized, don't sync.
163b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (!ShouldSyncVisit(transition, row))
164b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return;
165b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
166b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Create SyncChangeList.
167b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  syncer::SyncChangeList changes;
168b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
169b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  CreateOrUpdateSyncNode(*row, &changes);
170b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
171b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Send SyncChangeList to server if there are any changes.
172b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (changes.size() > 0)
173b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
174b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
175b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
176b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void TypedUrlSyncableService::OnUrlsDeleted(bool all_history,
17746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                                            bool expired,
178b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                                            URLRows* rows) {
17990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DCHECK(expected_loop_ == base::MessageLoop::current());
180b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
181b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (processing_syncer_changes_)
182b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return;  // These are changes originating from us, ignore.
183b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (!sync_processor_.get())
184b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return;  // Sync processor not yet initialized, don't sync.
185b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
18646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  // Ignore URLs expired due to old age (we don't want to sync them as deletions
18746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  // to avoid extra traffic up to the server, and also to make sure that a
18846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  // client with a bad clock setting won't go on an expiration rampage and
18946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  // delete all history from every client). The server will gracefully age out
19046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  // the sync DB entries when they've been idle for long enough.
19146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (expired)
192b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return;
193b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
194b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Create SyncChangeList.
195b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  syncer::SyncChangeList changes;
196b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
197b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (all_history) {
198b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // Delete all synced typed urls.
199b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    for (std::set<GURL>::const_iterator url = synced_typed_urls_.begin();
200b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)         url != synced_typed_urls_.end(); ++url) {
201b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      VisitVector visits;
202b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      URLRow row(*url);
203b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      AddTypedUrlToChangeList(syncer::SyncChange::ACTION_DELETE,
204b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                              row, visits, url->spec(), &changes);
205b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
206b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // Clear cache of server state.
207b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    synced_typed_urls_.clear();
208b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  } else {
209b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    DCHECK(rows);
210b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // Delete rows.
211b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    for (URLRows::const_iterator row = rows->begin();
212b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)         row != rows->end(); ++row) {
213b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      // Add specifics to change list for all synced urls that were deleted.
214b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      if (synced_typed_urls_.find(row->url()) != synced_typed_urls_.end()) {
215b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        VisitVector visits;
216b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        AddTypedUrlToChangeList(syncer::SyncChange::ACTION_DELETE,
217b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                                *row, visits, row->url().spec(), &changes);
218b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        // Delete typed url from cache.
219b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        synced_typed_urls_.erase(row->url());
220b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      }
221b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
222b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
223b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
224b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Send SyncChangeList to server if there are any changes.
225b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (changes.size() > 0)
226b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
227b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
228b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
229b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)bool TypedUrlSyncableService::ShouldIgnoreUrl(const GURL& url) {
230b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Ignore empty URLs. Not sure how this can happen (maybe import from other
231b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // busted browsers, or misuse of the history API, or just plain bugs) but we
232b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // can't deal with them.
233b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (url.spec().empty())
234b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return true;
235b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
236b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Ignore local file URLs.
237b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (url.SchemeIsFile())
238b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return true;
239b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
240b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Ignore localhost URLs.
241b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (net::IsLocalhost(url.host()))
242b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return true;
243b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
244b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return false;
245b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
246b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
247b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)bool TypedUrlSyncableService::ShouldSyncVisit(
248b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    content::PageTransition page_transition,
249b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    URLRow* row) {
250b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (!row)
251b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return false;
252b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  int typed_count = row->typed_count();
253b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  content::PageTransition transition = static_cast<content::PageTransition>(
254b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      page_transition & content::PAGE_TRANSITION_CORE_MASK);
255b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
256b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Just use an ad-hoc criteria to determine whether to ignore this
257b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // notification. For most users, the distribution of visits is roughly a bell
258b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // curve with a long tail - there are lots of URLs with < 5 visits so we want
259b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // to make sure we sync up every visit to ensure the proper ordering of
260b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // suggestions. But there are relatively few URLs with > 10 visits, and those
261b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // tend to be more broadly distributed such that there's no need to sync up
262b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // every visit to preserve their relative ordering.
263b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return (transition == content::PAGE_TRANSITION_TYPED &&
264b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)          typed_count > 0 &&
265b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)          (typed_count < kTypedUrlVisitThrottleThreshold ||
266b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)           (typed_count % kTypedUrlVisitThrottleMultiple) == 0));
267b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
268b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
269b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)bool TypedUrlSyncableService::CreateOrUpdateSyncNode(
270b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    URLRow url,
271b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    syncer::SyncChangeList* changes) {
272b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK_GT(url.typed_count(), 0);
273b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
274b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (ShouldIgnoreUrl(url.url()))
275b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return true;
276b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
277b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Get the visits for this node.
278b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  VisitVector visit_vector;
279b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (!FixupURLAndGetVisits(&url, &visit_vector)) {
280b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    DLOG(ERROR) << "Could not load visits for url: " << url.url();
281b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return false;
282b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
283b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK(!visit_vector.empty());
284b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
285b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  std::string title = url.url().spec();
286b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  syncer::SyncChange::SyncChangeType change_type;
287b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
288b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // If server already has URL, then send a sync update, else add it.
289b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  change_type =
290b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      (synced_typed_urls_.find(url.url()) != synced_typed_urls_.end()) ?
291b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      syncer::SyncChange::ACTION_UPDATE :
292b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      syncer::SyncChange::ACTION_ADD;
293b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
294b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Ensure cache of server state is up to date.
295b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  synced_typed_urls_.insert(url.url());
296b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
297b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  AddTypedUrlToChangeList(change_type, url, visit_vector, title, changes);
298b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
299b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return true;
300b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
301b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
302b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void TypedUrlSyncableService::AddTypedUrlToChangeList(
303b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    syncer::SyncChange::SyncChangeType change_type,
304b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const URLRow& row,
305b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const VisitVector& visits,
306b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    std::string title,
307b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    syncer::SyncChangeList* change_list) {
308b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  sync_pb::EntitySpecifics entity_specifics;
309b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
310b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
311b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (change_type == syncer::SyncChange::ACTION_DELETE) {
312b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    typed_url->set_url(row.url().spec());
313b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  } else {
314b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    WriteToTypedUrlSpecifics(row, visits, typed_url);
315b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
316b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
317b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  change_list->push_back(
318b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      syncer::SyncChange(FROM_HERE, change_type,
319b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                         syncer::SyncData::CreateLocalData(
320b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                             kTypedUrlTag, title, entity_specifics)));
321b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
322b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
323b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void TypedUrlSyncableService::WriteToTypedUrlSpecifics(
324b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const URLRow& url,
325b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const VisitVector& visits,
326b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    sync_pb::TypedUrlSpecifics* typed_url) {
327b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
328b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK(!url.last_visit().is_null());
329b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK(!visits.empty());
330b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK_EQ(url.last_visit().ToInternalValue(),
331b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)            visits.back().visit_time.ToInternalValue());
332b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
333b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  typed_url->set_url(url.url().spec());
3345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  typed_url->set_title(base::UTF16ToUTF8(url.title()));
335b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  typed_url->set_hidden(url.hidden());
336b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
337b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK(CheckVisitOrdering(visits));
338b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
339b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  bool only_typed = false;
340b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  int skip_count = 0;
341b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
342b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (visits.size() > static_cast<size_t>(kMaxTypedUrlVisits)) {
343b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    int typed_count = 0;
344b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    int total = 0;
345b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // Walk the passed-in visit vector and count the # of typed visits.
346b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    for (VisitVector::const_iterator visit = visits.begin();
347b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)         visit != visits.end(); ++visit) {
348b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      content::PageTransition transition = content::PageTransitionFromInt(
349b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)          visit->transition & content::PAGE_TRANSITION_CORE_MASK);
350b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      // We ignore reload visits.
351b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      if (transition == content::PAGE_TRANSITION_RELOAD)
352b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        continue;
353b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      ++total;
354b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      if (transition == content::PAGE_TRANSITION_TYPED)
355b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        ++typed_count;
356b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
357b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // We should have at least one typed visit. This can sometimes happen if
358b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // the history DB has an inaccurate count for some reason (there's been
359b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // bugs in the history code in the past which has left users in the wild
360b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // with incorrect counts - http://crbug.com/84258).
361b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    DCHECK(typed_count > 0);
362b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
363b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (typed_count > kMaxTypedUrlVisits) {
364b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      only_typed = true;
365b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      skip_count = typed_count - kMaxTypedUrlVisits;
366b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    } else if (total > kMaxTypedUrlVisits) {
367b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      skip_count = total - kMaxTypedUrlVisits;
368b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
369b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
370b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
371b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  for (VisitVector::const_iterator visit = visits.begin();
372b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)       visit != visits.end(); ++visit) {
373b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    content::PageTransition transition = content::PageTransitionFromInt(
374b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        visit->transition & content::PAGE_TRANSITION_CORE_MASK);
375b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // Skip reload visits.
376b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (transition == content::PAGE_TRANSITION_RELOAD)
377b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      continue;
378b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
379b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // If we only have room for typed visits, then only add typed visits.
380b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (only_typed && transition != content::PAGE_TRANSITION_TYPED)
381b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      continue;
382b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
383b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (skip_count > 0) {
384b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      // We have too many entries to fit, so we need to skip the oldest ones.
385b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      // Only skip typed URLs if there are too many typed URLs to fit.
386b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      if (only_typed || transition != content::PAGE_TRANSITION_TYPED) {
387b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        --skip_count;
388b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        continue;
389b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      }
390b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
391b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    typed_url->add_visits(visit->visit_time.ToInternalValue());
392b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    typed_url->add_visit_transitions(visit->transition);
393b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
394b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK_EQ(skip_count, 0);
395b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
396b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (typed_url->visits_size() == 0) {
397b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // If we get here, it's because we don't actually have any TYPED visits
398b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // even though the visit's typed_count > 0 (corrupted typed_count). So
399b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // let's go ahead and add a RELOAD visit at the most recent visit since
400b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // it's not legal to have an empty visit array (yet another workaround
401b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    // for http://crbug.com/84258).
402b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    typed_url->add_visits(url.last_visit().ToInternalValue());
403b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    typed_url->add_visit_transitions(content::PAGE_TRANSITION_RELOAD);
404b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
405b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  CHECK_GT(typed_url->visits_size(), 0);
406b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  CHECK_LE(typed_url->visits_size(), kMaxTypedUrlVisits);
407b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  CHECK_EQ(typed_url->visits_size(), typed_url->visit_transitions_size());
408b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
409b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
410b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)bool TypedUrlSyncableService::FixupURLAndGetVisits(
411b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    URLRow* url,
412b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    VisitVector* visits) {
413b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  ++num_db_accesses_;
414b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  CHECK(history_backend_);
415b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (!history_backend_->GetMostRecentVisitsForURL(
416b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)          url->id(), kMaxVisitsToFetch, visits)) {
417b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    ++num_db_errors_;
418b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return false;
419b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
420b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
421b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Sometimes (due to a bug elsewhere in the history or sync code, or due to
422b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // a crash between adding a URL to the history database and updating the
423b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // visit DB) the visit vector for a URL can be empty. If this happens, just
424b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // create a new visit whose timestamp is the same as the last_visit time.
425b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // This is a workaround for http://crbug.com/84258.
426b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (visits->empty()) {
427b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    DVLOG(1) << "Found empty visits for URL: " << url->url();
428b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    VisitRow visit(
429b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        url->id(), url->last_visit(), 0, content::PAGE_TRANSITION_TYPED, 0);
430b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    visits->push_back(visit);
431b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
432b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
433b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // GetMostRecentVisitsForURL() returns the data in the opposite order that
434b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // we need it, so reverse it.
435b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  std::reverse(visits->begin(), visits->end());
436b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
437b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Sometimes, the last_visit field in the URL doesn't match the timestamp of
438b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // the last visit in our visit array (they come from different tables, so
439b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // crashes/bugs can cause them to mismatch), so just set it here.
440b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  url->set_last_visit(visits->back().visit_time);
441b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DCHECK(CheckVisitOrdering(*visits));
442b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return true;
443b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
444b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
445b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}  // namespace history
446