15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sync/glue/typed_url_model_associator.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <set>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/location.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/metrics/histogram.h"
13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/history/history_backend.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sync/profile_sync_service.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_util.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/api/sync_error.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/internal_api/public/read_node.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/internal_api/public/read_transaction.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/internal_api/public/write_node.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/internal_api/public/write_transaction.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/protocol/typed_url_specifics.pb.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread;
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace browser_sync {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The server backend can't handle arbitrarily large node sizes, so to keep
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the size under control we limit the visit array.
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kMaxTypedUrlVisits = 100;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// There's no limit on how many visits the history DB could have for a given
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// typed URL, so we limit how many we fetch from the DB to avoid crashes due to
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// running out of memory (http://crbug.com/89793). This value is different
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// from kMaxTypedUrlVisits, as some of the visits fetched from the DB may be
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// RELOAD visits, which will be stripped.
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kMaxVisitsToFetch = 1000;
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kTypedUrlTag[] = "google_chrome_typed_urls";
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static bool CheckVisitOrdering(const history::VisitVector& visits) {
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 previous_visit_time = 0;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (history::VisitVector::const_iterator visit = visits.begin();
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       visit != visits.end(); ++visit) {
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (visit != visits.begin()) {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We allow duplicate visits here - they shouldn't really be allowed, but
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // they still seem to show up sometimes and we haven't figured out the
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // source, so we just log an error instead of failing an assertion.
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // (http://crbug.com/91473).
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (previous_visit_time == visit->visit_time.ToInternalValue())
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DVLOG(1) << "Duplicate visit time encountered";
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else if (previous_visit_time > visit->visit_time.ToInternalValue())
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    previous_visit_time = visit->visit_time.ToInternalValue();
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TypedUrlModelAssociator::TypedUrlModelAssociator(
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ProfileSyncService* sync_service,
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    history::HistoryBackend* history_backend,
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DataTypeErrorHandler* error_handler)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : sync_service_(sync_service),
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      history_backend_(history_backend),
6790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      expected_loop_(base::MessageLoop::current()),
68a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      abort_requested_(false),
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      error_handler_(error_handler),
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      num_db_accesses_(0),
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      num_db_errors_(0) {
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(sync_service_);
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // history_backend_ may be null for unit tests (since it's not mockable).
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TypedUrlModelAssociator::~TypedUrlModelAssociator() {}
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TypedUrlModelAssociator::FixupURLAndGetVisits(
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    history::URLRow* url,
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    history::VisitVector* visits) {
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ++num_db_accesses_;
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(history_backend_);
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!history_backend_->GetMostRecentVisitsForURL(
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          url->id(), kMaxVisitsToFetch, visits)) {
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++num_db_errors_;
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Sometimes (due to a bug elsewhere in the history or sync code, or due to
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // a crash between adding a URL to the history database and updating the
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // visit DB) the visit vector for a URL can be empty. If this happens, just
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // create a new visit whose timestamp is the same as the last_visit time.
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This is a workaround for http://crbug.com/84258.
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (visits->empty()) {
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DVLOG(1) << "Found empty visits for URL: " << url->url();
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    history::VisitRow visit(
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        url->id(), url->last_visit(), 0, content::PAGE_TRANSITION_TYPED, 0);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    visits->push_back(visit);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // GetMostRecentVisitsForURL() returns the data in the opposite order that
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // we need it, so reverse it.
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::reverse(visits->begin(), visits->end());
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Sometimes, the last_visit field in the URL doesn't match the timestamp of
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the last visit in our visit array (they come from different tables, so
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // crashes/bugs can cause them to mismatch), so just set it here.
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url->set_last_visit(visits->back().visit_time);
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(CheckVisitOrdering(*visits));
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TypedUrlModelAssociator::ShouldIgnoreUrl(const GURL& url) {
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Ignore empty URLs. Not sure how this can happen (maybe import from other
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // busted browsers, or misuse of the history API, or just plain bugs) but we
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // can't deal with them.
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (url.spec().empty())
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Ignore local file URLs.
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (url.SchemeIsFile())
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Ignore localhost URLs.
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (net::IsLocalhost(url.host()))
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TypedUrlModelAssociator::ShouldIgnoreVisits(
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const history::VisitVector& visits) {
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We ignore URLs that were imported, but have never been visited by
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // chromium.
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const int kLastImportedSource = history::SOURCE_EXTENSION;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  history::VisitSourceMap map;
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!history_backend_->GetVisitsSource(visits, &map))
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;  // If we can't read the visit, assume it's not imported.
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Walk the list of visits and look for a non-imported item.
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (history::VisitVector::const_iterator it = visits.begin();
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != visits.end(); ++it) {
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (map.count(it->visit_id) == 0 ||
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        map[it->visit_id] <= kLastImportedSource) {
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We only saw imported visits, so tell the caller to ignore them.
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)syncer::SyncError TypedUrlModelAssociator::AssociateModels(
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::SyncMergeResult* local_merge_result,
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::SyncMergeResult* syncer_merge_result) {
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ClearErrorStats();
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::SyncError error = DoAssociateModels();
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UMA_HISTOGRAM_PERCENTAGE("Sync.TypedUrlModelAssociationErrors",
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           GetErrorPercentage());
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ClearErrorStats();
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return error;
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TypedUrlModelAssociator::ClearErrorStats() {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  num_db_accesses_ = 0;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  num_db_errors_ = 0;
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int TypedUrlModelAssociator::GetErrorPercentage() const {
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return num_db_accesses_ ? (100 * num_db_errors_ / num_db_accesses_) : 0;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)syncer::SyncError TypedUrlModelAssociator::DoAssociateModels() {
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DVLOG(1) << "Associating TypedUrl Models";
17690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DCHECK(expected_loop_ == base::MessageLoop::current());
177a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  history::URLRows typed_urls;
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ++num_db_accesses_;
180a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  bool query_succeeded =
181a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      history_backend_ && history_backend_->GetAllTypedURLs(&typed_urls);
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  history::URLRows new_urls;
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TypedUrlVisitVector new_visits;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TypedUrlUpdateVector updated_urls;
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  {
187a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    base::AutoLock au(abort_lock_);
188a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    if (abort_requested_) {
189a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      return syncer::SyncError(FROM_HERE,
190a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                               syncer::SyncError::DATATYPE_ERROR,
191a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                               "Association was aborted.",
192a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                               model_type());
193a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
194a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
195a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // Must lock and check first to make sure |error_handler_| is valid.
196a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    if (!query_succeeded) {
197a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      ++num_db_errors_;
198a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      return error_handler_->CreateAndUploadError(
199a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)          FROM_HERE,
200a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)          "Could not get the typed_url entries.",
201a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)          model_type());
202a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
203a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
204a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // Get all the visits.
205a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    std::map<history::URLID, history::VisitVector> visit_vectors;
206a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    for (history::URLRows::iterator ix = typed_urls.begin();
207a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)         ix != typed_urls.end();) {
208a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      DCHECK_EQ(0U, visit_vectors.count(ix->id()));
209a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      if (!FixupURLAndGetVisits(&(*ix), &(visit_vectors[ix->id()])) ||
210a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)          ShouldIgnoreUrl(ix->url()) ||
211a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)          ShouldIgnoreVisits(visit_vectors[ix->id()])) {
212a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        // Ignore this URL if we couldn't load the visits or if there's some
213a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        // other problem with it (it was empty, or imported and never visited).
214a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        ix = typed_urls.erase(ix);
215a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      } else {
216a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        ++ix;
217a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      }
218a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
219a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    syncer::ReadNode typed_url_root(&trans);
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (typed_url_root.InitByTagLookup(kTypedUrlTag) !=
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            syncer::BaseNode::INIT_OK) {
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return error_handler_->CreateAndUploadError(
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          FROM_HERE,
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "Server did not create the top-level typed_url node. We "
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "might be running against an out-of-date server.",
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          model_type());
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::set<std::string> current_urls;
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (history::URLRows::iterator ix = typed_urls.begin();
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ix != typed_urls.end(); ++ix) {
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string tag = ix->url().spec();
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Empty URLs should be filtered out by ShouldIgnoreUrl() previously.
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DCHECK(!tag.empty());
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      history::VisitVector& visits = visit_vectors[ix->id()];
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      syncer::ReadNode node(&trans);
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (node.InitByClientTagLookup(syncer::TYPED_URLS, tag) ==
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              syncer::BaseNode::INIT_OK) {
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Same URL exists in sync data and in history data - compare the
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // entries to see if there's any difference.
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        sync_pb::TypedUrlSpecifics typed_url(
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            FilterExpiredVisits(node.GetTypedUrlSpecifics()));
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK_EQ(tag, typed_url.url());
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Initialize fields in |new_url| to the same values as the fields in
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // the existing URLRow in the history DB. This is needed because we
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // overwrite the existing value below in WriteToHistoryBackend(), but
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // some of the values in that structure are not synced (like
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // typed_count).
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        history::URLRow new_url(*ix);
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::vector<history::VisitInfo> added_visits;
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        MergeResult difference =
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            MergeUrls(typed_url, *ix, &visits, &new_url, &added_visits);
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (difference & DIFF_UPDATE_NODE) {
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          syncer::WriteNode write_node(&trans);
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (write_node.InitByClientTagLookup(syncer::TYPED_URLS, tag) !=
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  syncer::BaseNode::INIT_OK) {
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            return error_handler_->CreateAndUploadError(
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                FROM_HERE,
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                "Failed to edit typed_url sync node.",
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                model_type());
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // We don't want to resurrect old visits that have been aged out by
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // other clients, so remove all visits that are older than the
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // earliest existing visit in the sync node.
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (typed_url.visits_size() > 0) {
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            base::Time earliest_visit =
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                base::Time::FromInternalValue(typed_url.visits(0));
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            for (history::VisitVector::iterator it = visits.begin();
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 it != visits.end() && it->visit_time < earliest_visit; ) {
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              it = visits.erase(it);
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            }
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            // Should never be possible to delete all the items, since the
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            // visit vector contains all the items in typed_url.visits.
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            DCHECK(visits.size() > 0);
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          DCHECK_EQ(new_url.last_visit().ToInternalValue(),
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    visits.back().visit_time.ToInternalValue());
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          WriteToSyncNode(new_url, visits, &write_node);
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (difference & DIFF_LOCAL_ROW_CHANGED) {
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          updated_urls.push_back(
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              std::pair<history::URLID, history::URLRow>(ix->id(), new_url));
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (difference & DIFF_LOCAL_VISITS_ADDED) {
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          new_visits.push_back(
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              std::pair<GURL, std::vector<history::VisitInfo> >(ix->url(),
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                                added_visits));
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Sync has never seen this URL before.
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        syncer::WriteNode node(&trans);
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        syncer::WriteNode::InitUniqueByCreationResult result =
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            node.InitUniqueByCreation(syncer::TYPED_URLS,
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      typed_url_root, tag);
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (result != syncer::WriteNode::INIT_SUCCESS) {
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return error_handler_->CreateAndUploadError(
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              FROM_HERE,
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              "Failed to create typed_url sync node: " + tag,
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              model_type());
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        node.SetTitle(UTF8ToWide(tag));
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        WriteToSyncNode(*ix, visits, &node);
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_urls.insert(tag);
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Now walk the sync nodes and detect any URLs that exist there, but not in
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the history DB, so we can add them to our local history DB.
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<int64> obsolete_nodes;
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int64 sync_child_id = typed_url_root.GetFirstChildId();
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (sync_child_id != syncer::kInvalidId) {
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      syncer::ReadNode sync_child_node(&trans);
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (sync_child_node.InitByIdLookup(sync_child_id) !=
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              syncer::BaseNode::INIT_OK) {
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return error_handler_->CreateAndUploadError(
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            FROM_HERE,
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            "Failed to fetch child node.",
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            model_type());
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const sync_pb::TypedUrlSpecifics& typed_url(
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          sync_child_node.GetTypedUrlSpecifics());
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sync_child_id = sync_child_node.GetSuccessorId();
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Ignore old sync nodes that don't have any transition data stored with
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // them, or transition data that does not match the visit data (will be
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // deleted below).
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (typed_url.visit_transitions_size() == 0 ||
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          typed_url.visit_transitions_size() != typed_url.visits_size()) {
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Generate a debug assertion to help track down http://crbug.com/91473,
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // even though we gracefully handle this case by throwing away this
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // node.
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK_EQ(typed_url.visits_size(), typed_url.visit_transitions_size());
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DVLOG(1) << "Deleting obsolete sync node with no visit "
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << "transition info.";
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        obsolete_nodes.push_back(sync_child_node.GetId());
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue;
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (typed_url.url().empty()) {
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DVLOG(1) << "Ignoring empty URL in sync DB";
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue;
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Now, get rid of the expired visits, and if there are no un-expired
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // visits left, just ignore this node.
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sync_pb::TypedUrlSpecifics filtered_url = FilterExpiredVisits(typed_url);
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (filtered_url.visits_size() == 0) {
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DVLOG(1) << "Ignoring expired URL in sync DB: " << filtered_url.url();
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue;
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (current_urls.find(filtered_url.url()) == current_urls.end()) {
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Update the local DB from the sync DB. Since we are doing our
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // initial model association, we don't want to remove any of the
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // existing visits (pass NULL as |visits_to_remove|).
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        UpdateFromSyncDB(filtered_url,
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         &new_visits,
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         NULL,
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         &updated_urls,
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         &new_urls);
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If we encountered any obsolete nodes, remove them so they don't hang
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // around and confuse people looking at the sync node browser.
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!obsolete_nodes.empty()) {
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (std::vector<int64>::const_iterator it = obsolete_nodes.begin();
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           it != obsolete_nodes.end();
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           ++it) {
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        syncer::WriteNode sync_node(&trans);
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (sync_node.InitByIdLookup(*it) != syncer::BaseNode::INIT_OK) {
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return error_handler_->CreateAndUploadError(
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              FROM_HERE,
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              "Failed to fetch obsolete node.",
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              model_type());
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        sync_node.Tombstone();
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Since we're on the history thread, we don't have to worry about updating
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the history database after closing the write transaction, since
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // this is the only thread that writes to the database.  We also don't have
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // to worry about the sync model getting out of sync, because changes are
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // propagated to the ChangeProcessor on this thread.
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WriteToHistoryBackend(&new_urls, &updated_urls, &new_visits, NULL);
396a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  return syncer::SyncError();
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TypedUrlModelAssociator::UpdateFromSyncDB(
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::TypedUrlSpecifics& typed_url,
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TypedUrlVisitVector* visits_to_add,
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    history::VisitVector* visits_to_remove,
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TypedUrlUpdateVector* updated_urls,
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    history::URLRows* new_urls) {
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  history::URLRow new_url(GURL(typed_url.url()));
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  history::VisitVector existing_visits;
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool existing_url = history_backend_->GetURL(new_url.url(), &new_url);
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (existing_url) {
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // This URL already exists locally - fetch the visits so we can
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // merge them below.
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!FixupURLAndGetVisits(&new_url, &existing_visits)) {
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Couldn't load the visits for this URL due to some kind of DB error.
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Don't bother writing this URL to the history DB (if we ignore the
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // error and continue, we might end up duplicating existing visits).
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DLOG(ERROR) << "Could not load visits for url: " << new_url.url();
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  visits_to_add->push_back(std::pair<GURL, std::vector<history::VisitInfo> >(
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_url.url(), std::vector<history::VisitInfo>()));
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Update the URL with information from the typed URL.
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateURLRowFromTypedUrlSpecifics(typed_url, &new_url);
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Figure out which visits we need to add.
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DiffVisits(existing_visits, typed_url, &visits_to_add->back().second,
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             visits_to_remove);
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (existing_url) {
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    updated_urls->push_back(
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::pair<history::URLID, history::URLRow>(new_url.id(), new_url));
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_urls->push_back(new_url);
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sync_pb::TypedUrlSpecifics TypedUrlModelAssociator::FilterExpiredVisits(
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::TypedUrlSpecifics& source) {
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Make a copy of the source, then regenerate the visits.
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::TypedUrlSpecifics specifics(source);
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  specifics.clear_visits();
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  specifics.clear_visit_transitions();
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < source.visits_size(); ++i) {
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::Time time = base::Time::FromInternalValue(source.visits(i));
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!history_backend_->IsExpiredVisitTime(time)) {
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      specifics.add_visits(source.visits(i));
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      specifics.add_visit_transitions(source.visit_transitions(i));
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(specifics.visits_size() == specifics.visit_transitions_size());
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return specifics;
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TypedUrlModelAssociator::DeleteAllNodes(
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    syncer::WriteTransaction* trans) {
45690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DCHECK(expected_loop_ == base::MessageLoop::current());
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Just walk through all our child nodes and delete them.
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::ReadNode typed_url_root(trans);
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (typed_url_root.InitByTagLookup(kTypedUrlTag) !=
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          syncer::BaseNode::INIT_OK) {
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Could not lookup root node";
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 sync_child_id = typed_url_root.GetFirstChildId();
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (sync_child_id != syncer::kInvalidId) {
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    syncer::WriteNode sync_child_node(trans);
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (sync_child_node.InitByIdLookup(sync_child_id) !=
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            syncer::BaseNode::INIT_OK) {
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "Typed url node lookup failed.";
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sync_child_id = sync_child_node.GetSuccessorId();
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_child_node.Tombstone();
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)syncer::SyncError TypedUrlModelAssociator::DisassociateModels() {
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return syncer::SyncError();
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TypedUrlModelAssociator::AbortAssociation() {
484a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  base::AutoLock lock(abort_lock_);
485a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  abort_requested_ = true;
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TypedUrlModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(has_nodes);
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *has_nodes = false;
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::ReadNode sync_node(&trans);
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (sync_node.InitByTagLookup(kTypedUrlTag) != syncer::BaseNode::INIT_OK) {
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Server did not create the top-level typed_url node. We "
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "might be running against an out-of-date server.";
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The sync model has user created nodes if the typed_url folder has any
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // children.
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *has_nodes = sync_node.HasChildren();
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TypedUrlModelAssociator::WriteToHistoryBackend(
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const history::URLRows* new_urls,
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const TypedUrlUpdateVector* updated_urls,
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const TypedUrlVisitVector* new_visits,
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const history::VisitVector* deleted_visits) {
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (new_urls) {
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    history_backend_->AddPagesWithDetails(*new_urls, history::SOURCE_SYNCED);
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (updated_urls) {
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (TypedUrlUpdateVector::const_iterator url = updated_urls->begin();
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         url != updated_urls->end(); ++url) {
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // This is an existing entry in the URL database. We don't verify the
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // visit_count or typed_count values here, because either one (or both)
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // could be zero in the case of bookmarks, or in the case of a URL
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // transitioning from non-typed to typed as a result of this sync.
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++num_db_accesses_;
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!history_backend_->UpdateURL(url->first, url->second)) {
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // In the field we sometimes run into errors on specific URLs. It's OK
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // to just continue on (we can try writing again on the next model
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // association).
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ++num_db_errors_;
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DLOG(ERROR) << "Could not update page: " << url->second.url().spec();
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (new_visits) {
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (TypedUrlVisitVector::const_iterator visits = new_visits->begin();
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         visits != new_visits->end(); ++visits) {
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If there are no visits to add, just skip this.
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (visits->second.empty())
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue;
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++num_db_accesses_;
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!history_backend_->AddVisits(visits->first, visits->second,
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       history::SOURCE_SYNCED)) {
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ++num_db_errors_;
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DLOG(ERROR) << "Could not add visits.";
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (deleted_visits) {
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++num_db_accesses_;
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!history_backend_->RemoveVisits(*deleted_visits)) {
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++num_db_errors_;
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DLOG(ERROR) << "Could not remove visits.";
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // This is bad news, since it means we may end up resurrecting history
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // entries on the next reload. It's unavoidable so we'll just keep on
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // syncing.
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TypedUrlModelAssociator::MergeResult TypedUrlModelAssociator::MergeUrls(
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::TypedUrlSpecifics& node,
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const history::URLRow& url,
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    history::VisitVector* visits,
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    history::URLRow* new_url,
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<history::VisitInfo>* new_visits) {
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(new_url);
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!node.url().compare(url.url().spec()));
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!node.url().compare(new_url->url().spec()));
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(visits->size());
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK_EQ(node.visits_size(), node.visit_transitions_size());
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we have an old-format node (before we added the visits and
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // visit_transitions arrays to the protobuf) or else the node only contained
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // expired visits, so just overwrite it with our local history data.
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (node.visits_size() == 0)
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return DIFF_UPDATE_NODE;
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Convert these values only once.
576a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 node_title(UTF8ToUTF16(node.title()));
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Time node_last_visit = base::Time::FromInternalValue(
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      node.visits(node.visits_size() - 1));
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This is a bitfield representing what we'll need to update with the output
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // value.
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MergeResult different = DIFF_NONE;
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check if the non-incremented values changed.
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((node_title.compare(url.title()) != 0) ||
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (node.hidden() != url.hidden())) {
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Use the values from the most recent visit.
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (node_last_visit >= url.last_visit()) {
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_url->set_title(node_title);
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_url->set_hidden(node.hidden());
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      different |= DIFF_LOCAL_ROW_CHANGED;
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_url->set_title(url.title());
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_url->set_hidden(url.hidden());
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      different |= DIFF_UPDATE_NODE;
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // No difference.
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_url->set_title(url.title());
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_url->set_hidden(url.hidden());
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t node_num_visits = node.visits_size();
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t history_num_visits = visits->size();
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t node_visit_index = 0;
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t history_visit_index = 0;
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Time earliest_history_time = (*visits)[0].visit_time;
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Walk through the two sets of visits and figure out if any new visits were
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // added on either side.
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (node_visit_index < node_num_visits ||
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         history_visit_index < history_num_visits) {
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Time objects are initialized to "earliest possible time".
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::Time node_time, history_time;
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (node_visit_index < node_num_visits)
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      node_time = base::Time::FromInternalValue(node.visits(node_visit_index));
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (history_visit_index < history_num_visits)
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      history_time = (*visits)[history_visit_index].visit_time;
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (node_visit_index >= node_num_visits ||
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (history_visit_index < history_num_visits &&
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         node_time > history_time)) {
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We found a visit in the history DB that doesn't exist in the sync DB,
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // so mark the node as modified so the caller will update the sync node.
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      different |= DIFF_UPDATE_NODE;
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++history_visit_index;
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (history_visit_index >= history_num_visits ||
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               node_time < history_time) {
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Found a visit in the sync node that doesn't exist in the history DB, so
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // add it to our list of new visits and set the appropriate flag so the
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // caller will update the history DB.
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If the node visit is older than any existing visit in the history DB,
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // don't re-add it - this keeps us from resurrecting visits that were
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // aged out locally.
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (node_time > earliest_history_time) {
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        different |= DIFF_LOCAL_VISITS_ADDED;
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        new_visits->push_back(history::VisitInfo(
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            node_time,
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            content::PageTransitionFromInt(
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                node.visit_transitions(node_visit_index))));
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // This visit is added to visits below.
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++node_visit_index;
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Same (already synced) entry found in both DBs - no need to do anything.
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++node_visit_index;
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++history_visit_index;
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(CheckVisitOrdering(*visits));
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (different & DIFF_LOCAL_VISITS_ADDED) {
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Insert new visits into the apropriate place in the visits vector.
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    history::VisitVector::iterator visit_ix = visits->begin();
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (std::vector<history::VisitInfo>::iterator new_visit =
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             new_visits->begin();
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         new_visit != new_visits->end(); ++new_visit) {
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (visit_ix != visits->end() &&
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             new_visit->first > visit_ix->visit_time) {
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ++visit_ix;
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      visit_ix = visits->insert(visit_ix,
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                history::VisitRow(url.id(), new_visit->first,
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                  0, new_visit->second, 0));
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++visit_ix;
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(CheckVisitOrdering(*visits));
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_url->set_last_visit(visits->back().visit_time);
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return different;
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TypedUrlModelAssociator::WriteToSyncNode(
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const history::URLRow& url,
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const history::VisitVector& visits,
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    syncer::WriteNode* node) {
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sync_pb::TypedUrlSpecifics typed_url;
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WriteToTypedUrlSpecifics(url, visits, &typed_url);
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  node->SetTypedUrlSpecifics(typed_url);
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TypedUrlModelAssociator::WriteToTypedUrlSpecifics(
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const history::URLRow& url,
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const history::VisitVector& visits,
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sync_pb::TypedUrlSpecifics* typed_url) {
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!url.last_visit().is_null());
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!visits.empty());
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(url.last_visit().ToInternalValue(),
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            visits.back().visit_time.ToInternalValue());
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typed_url->set_url(url.url().spec());
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typed_url->set_title(UTF16ToUTF8(url.title()));
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typed_url->set_hidden(url.hidden());
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(CheckVisitOrdering(visits));
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool only_typed = false;
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int skip_count = 0;
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (visits.size() > static_cast<size_t>(kMaxTypedUrlVisits)) {
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int typed_count = 0;
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int total = 0;
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Walk the passed-in visit vector and count the # of typed visits.
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (history::VisitVector::const_iterator visit = visits.begin();
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         visit != visits.end(); ++visit) {
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::PageTransition transition = content::PageTransitionFromInt(
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          visit->transition & content::PAGE_TRANSITION_CORE_MASK);
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We ignore reload visits.
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (transition == content::PAGE_TRANSITION_RELOAD)
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue;
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++total;
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (transition == content::PAGE_TRANSITION_TYPED)
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ++typed_count;
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We should have at least one typed visit. This can sometimes happen if
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the history DB has an inaccurate count for some reason (there's been
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // bugs in the history code in the past which has left users in the wild
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // with incorrect counts - http://crbug.com/84258).
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(typed_count > 0);
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (typed_count > kMaxTypedUrlVisits) {
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      only_typed = true;
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      skip_count = typed_count - kMaxTypedUrlVisits;
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (total > kMaxTypedUrlVisits) {
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      skip_count = total - kMaxTypedUrlVisits;
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (history::VisitVector::const_iterator visit = visits.begin();
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       visit != visits.end(); ++visit) {
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content::PageTransition transition = content::PageTransitionFromInt(
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        visit->transition & content::PAGE_TRANSITION_CORE_MASK);
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Skip reload visits.
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (transition == content::PAGE_TRANSITION_RELOAD)
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If we only have room for typed visits, then only add typed visits.
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (only_typed && transition != content::PAGE_TRANSITION_TYPED)
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (skip_count > 0) {
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We have too many entries to fit, so we need to skip the oldest ones.
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Only skip typed URLs if there are too many typed URLs to fit.
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (only_typed || transition != content::PAGE_TRANSITION_TYPED) {
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        --skip_count;
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue;
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    typed_url->add_visits(visit->visit_time.ToInternalValue());
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    typed_url->add_visit_transitions(visit->transition);
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(skip_count, 0);
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (typed_url->visits_size() == 0) {
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If we get here, it's because we don't actually have any TYPED visits
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // even though the visit's typed_count > 0 (corrupted typed_count). So
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // let's go ahead and add a RELOAD visit at the most recent visit since
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // it's not legal to have an empty visit array (yet another workaround
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // for http://crbug.com/84258).
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    typed_url->add_visits(url.last_visit().ToInternalValue());
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    typed_url->add_visit_transitions(content::PAGE_TRANSITION_RELOAD);
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK_GT(typed_url->visits_size(), 0);
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK_LE(typed_url->visits_size(), kMaxTypedUrlVisits);
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK_EQ(typed_url->visits_size(), typed_url->visit_transitions_size());
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TypedUrlModelAssociator::DiffVisits(
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const history::VisitVector& old_visits,
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::TypedUrlSpecifics& new_url,
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<history::VisitInfo>* new_visits,
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    history::VisitVector* removed_visits) {
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(new_visits);
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t old_visit_count = old_visits.size();
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t new_visit_count = new_url.visits_size();
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t old_index = 0;
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t new_index = 0;
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (old_index < old_visit_count && new_index < new_visit_count) {
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::Time new_visit_time =
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Time::FromInternalValue(new_url.visits(new_index));
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (old_visits[old_index].visit_time < new_visit_time) {
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (new_index > 0 && removed_visits) {
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If there are visits missing from the start of the node, that
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // means that they were probably clipped off due to our code that
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // limits the size of the sync nodes - don't delete them from our
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // local history.
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        removed_visits->push_back(old_visits[old_index]);
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++old_index;
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (old_visits[old_index].visit_time > new_visit_time) {
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_visits->push_back(history::VisitInfo(
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          new_visit_time,
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          content::PageTransitionFromInt(
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              new_url.visit_transitions(new_index))));
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++new_index;
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++old_index;
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++new_index;
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (removed_visits) {
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for ( ; old_index < old_visit_count; ++old_index) {
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      removed_visits->push_back(old_visits[old_index]);
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for ( ; new_index < new_visit_count; ++new_index) {
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_visits->push_back(history::VisitInfo(
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Time::FromInternalValue(new_url.visits(new_index)),
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        content::PageTransitionFromInt(new_url.visit_transitions(new_index))));
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TypedUrlModelAssociator::UpdateURLRowFromTypedUrlSpecifics(
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const sync_pb::TypedUrlSpecifics& typed_url, history::URLRow* new_url) {
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_GT(typed_url.visits_size(), 0);
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK_EQ(typed_url.visit_transitions_size(), typed_url.visits_size());
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_url->set_title(UTF8ToUTF16(typed_url.title()));
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_url->set_hidden(typed_url.hidden());
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only provide the initial value for the last_visit field - after that, let
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the history code update the last_visit field on its own.
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (new_url->last_visit().is_null()) {
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_url->set_last_visit(base::Time::FromInternalValue(
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        typed_url.visits(typed_url.visits_size() - 1)));
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TypedUrlModelAssociator::CryptoReadyIfNecessary() {
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We only access the cryptographer while holding a transaction.
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const syncer::ModelTypeSet encrypted_types = trans.GetEncryptedTypes();
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !encrypted_types.Has(syncer::TYPED_URLS) ||
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         sync_service_->IsCryptographerReady(&trans);
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace browser_sync
843