1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/glue/typed_url_change_processor.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h"
83345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/utf_string_conversions.h"
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/history/history_backend.h"
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/history/history_notifications.h"
1121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/glue/typed_url_model_associator.h"
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/profile_sync_service.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/protocol/typed_url_specifics.pb.h"
15ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_service.h"
16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_type.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace browser_sync {
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTypedUrlChangeProcessor::TypedUrlChangeProcessor(
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TypedUrlModelAssociator* model_associator,
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    history::HistoryBackend* history_backend,
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UnrecoverableErrorHandler* error_handler)
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : ChangeProcessor(error_handler),
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      model_associator_(model_associator),
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      history_backend_(history_backend),
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      observing_(false),
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      expected_loop_(MessageLoop::current()) {
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(model_associator);
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(history_backend);
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(error_handler);
32731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // When running in unit tests, there is already a NotificationService object.
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Since only one can exist at a time per thread, check first.
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!NotificationService::current())
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    notification_service_.reset(new NotificationService);
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StartObserving();
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTypedUrlChangeProcessor::~TypedUrlChangeProcessor() {
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(expected_loop_ == MessageLoop::current());
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TypedUrlChangeProcessor::Observe(NotificationType type,
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                      const NotificationSource& source,
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                      const NotificationDetails& details) {
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(expected_loop_ == MessageLoop::current());
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!observing_)
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
51731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  VLOG(1) << "Observed typed_url change.";
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(running());
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(NotificationType::HISTORY_TYPED_URLS_MODIFIED == type ||
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch         NotificationType::HISTORY_URLS_DELETED == type ||
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch         NotificationType::HISTORY_URL_VISITED == type);
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (type == NotificationType::HISTORY_TYPED_URLS_MODIFIED) {
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    HandleURLsModified(Details<history::URLsModifiedDetails>(details).ptr());
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (type == NotificationType::HISTORY_URLS_DELETED) {
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    HandleURLsDeleted(Details<history::URLsDeletedDetails>(details).ptr());
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (type == NotificationType::HISTORY_URL_VISITED) {
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    HandleURLsVisited(Details<history::URLVisitedDetails>(details).ptr());
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TypedUrlChangeProcessor::HandleURLsModified(
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    history::URLsModifiedDetails* details) {
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Get all the visits.
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::map<history::URLID, history::VisitVector> visit_vectors;
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (std::vector<history::URLRow>::iterator url =
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       details->changed_urls.begin(); url != details->changed_urls.end();
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       ++url) {
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!history_backend_->GetVisitsForURL(url->id(),
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                           &(visit_vectors[url->id()]))) {
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      error_handler()->OnUnrecoverableError(FROM_HERE,
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          "Could not get the url's visits.");
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    DCHECK(!visit_vectors[url->id()].empty());
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_api::WriteTransaction trans(share_handle());
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_api::ReadNode typed_url_root(&trans);
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!typed_url_root.InitByTagLookup(kTypedUrlTag)) {
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    error_handler()->OnUnrecoverableError(FROM_HERE,
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        "Server did not create the top-level typed_url node. We "
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch         "might be running against an out-of-date server.");
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (std::vector<history::URLRow>::iterator url =
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       details->changed_urls.begin(); url != details->changed_urls.end();
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       ++url) {
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::string tag = url->url().spec();
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    history::VisitVector& visits = visit_vectors[url->id()];
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    DCHECK(!visits.empty());
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    DCHECK(static_cast<size_t>(url->visit_count()) == visits.size());
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (static_cast<size_t>(url->visit_count()) != visits.size()) {
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      error_handler()->OnUnrecoverableError(FROM_HERE,
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          "Visit count does not match.");
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    sync_api::WriteNode update_node(&trans);
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (update_node.InitByClientTagLookup(syncable::TYPED_URLS, tag)) {
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      model_associator_->WriteToSyncNode(*url, visits, &update_node);
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      sync_api::WriteNode create_node(&trans);
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!create_node.InitUniqueByCreation(syncable::TYPED_URLS,
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                            typed_url_root, tag)) {
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        error_handler()->OnUnrecoverableError(FROM_HERE,
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            "Failed to create typed_url sync node.");
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return;
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      create_node.SetTitle(UTF8ToWide(tag));
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      model_associator_->WriteToSyncNode(*url, visits, &create_node);
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      model_associator_->Associate(&tag, create_node.GetId());
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TypedUrlChangeProcessor::HandleURLsDeleted(
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    history::URLsDeletedDetails* details) {
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_api::WriteTransaction trans(share_handle());
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (details->all_history) {
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!model_associator_->DeleteAllNodes(&trans)) {
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      error_handler()->OnUnrecoverableError(FROM_HERE, std::string());
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (std::set<GURL>::iterator url = details->urls.begin();
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch         url != details->urls.end(); ++url) {
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      sync_api::WriteNode sync_node(&trans);
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      int64 sync_id =
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      model_associator_->GetSyncIdFromChromeId(url->spec());
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (sync_api::kInvalidId != sync_id) {
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        if (!sync_node.InitByIdLookup(sync_id)) {
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          error_handler()->OnUnrecoverableError(FROM_HERE,
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              "Typed url node lookup failed.");
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          return;
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        }
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        model_associator_->Disassociate(sync_node.GetId());
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        sync_node.Remove();
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TypedUrlChangeProcessor::HandleURLsVisited(
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    history::URLVisitedDetails* details) {
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!details->row.typed_count()) {
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We only care about typed urls.
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  history::VisitVector visits;
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!history_backend_->GetVisitsForURL(details->row.id(), &visits) ||
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      visits.empty()) {
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    error_handler()->OnUnrecoverableError(FROM_HERE,
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        "Could not get the url's visits.");
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(static_cast<size_t>(details->row.visit_count()) == visits.size());
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_api::WriteTransaction trans(share_handle());
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string tag = details->row.url().spec();
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_api::WriteNode update_node(&trans);
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!update_node.InitByClientTagLookup(syncable::TYPED_URLS, tag)) {
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // If we don't know about it yet, it will be added later.
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_pb::TypedUrlSpecifics typed_url(update_node.GetTypedUrlSpecifics());
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  typed_url.add_visit(visits.back().visit_time.ToInternalValue());
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  update_node.SetTypedUrlSpecifics(typed_url);
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TypedUrlChangeProcessor::ApplyChangesFromSyncModel(
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const sync_api::BaseTransaction* trans,
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const sync_api::SyncManager::ChangeRecord* changes,
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int change_count) {
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(expected_loop_ == MessageLoop::current());
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!running())
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StopObserving();
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_api::ReadNode typed_url_root(trans);
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!typed_url_root.InitByTagLookup(kTypedUrlTag)) {
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    error_handler()->OnUnrecoverableError(FROM_HERE,
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        "TypedUrl root node lookup failed.");
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TypedUrlModelAssociator::TypedUrlTitleVector titles;
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TypedUrlModelAssociator::TypedUrlVector new_urls;
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TypedUrlModelAssociator::TypedUrlVisitVector new_visits;
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  history::VisitVector deleted_visits;
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TypedUrlModelAssociator::TypedUrlUpdateVector updated_urls;
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0; i < change_count; ++i) {
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        changes[i].action) {
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      DCHECK(changes[i].specifics.HasExtension(sync_pb::typed_url)) <<
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          "Typed URL delete change does not have necessary specifics.";
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      GURL url(changes[i].specifics.GetExtension(sync_pb::typed_url).url());
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      history_backend_->DeleteURL(url);
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      continue;
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    sync_api::ReadNode sync_node(trans);
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!sync_node.InitByIdLookup(changes[i].id)) {
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      error_handler()->OnUnrecoverableError(FROM_HERE,
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          "TypedUrl node lookup failed.");
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Check that the changed node is a child of the typed_urls folder.
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    DCHECK(typed_url_root.GetId() == sync_node.GetParentId());
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    DCHECK(syncable::TYPED_URLS == sync_node.GetModelType());
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const sync_pb::TypedUrlSpecifics& typed_url(
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        sync_node.GetTypedUrlSpecifics());
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GURL url(typed_url.url());
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (sync_api::SyncManager::ChangeRecord::ACTION_ADD == changes[i].action) {
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      DCHECK(typed_url.visit_size());
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!typed_url.visit_size()) {
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        continue;
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      history::URLRow new_url(url);
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new_url.set_title(UTF8ToUTF16(typed_url.title()));
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // When we add a new url, the last visit is always added, thus we set
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // the initial visit count to one.  This value will be automatically
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // incremented as visits are added.
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new_url.set_visit_count(1);
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new_url.set_typed_count(typed_url.typed_count());
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new_url.set_hidden(typed_url.hidden());
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new_url.set_last_visit(base::Time::FromInternalValue(
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          typed_url.visit(typed_url.visit_size() - 1)));
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new_urls.push_back(new_url);
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // The latest visit gets added automatically, so skip it.
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      std::vector<base::Time> added_visits;
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      for (int c = 0; c < typed_url.visit_size() - 1; ++c) {
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        DCHECK(typed_url.visit(c) < typed_url.visit(c + 1));
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        added_visits.push_back(
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            base::Time::FromInternalValue(typed_url.visit(c)));
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new_visits.push_back(
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          std::pair<GURL, std::vector<base::Time> >(url, added_visits));
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      history::URLRow old_url;
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!history_backend_->GetURL(url, &old_url)) {
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        error_handler()->OnUnrecoverableError(FROM_HERE,
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            "TypedUrl db lookup failed.");
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return;
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      history::VisitVector visits;
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!history_backend_->GetVisitsForURL(old_url.id(), &visits)) {
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        error_handler()->OnUnrecoverableError(FROM_HERE,
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            "Could not get the url's visits.");
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return;
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      history::URLRow new_url(url);
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new_url.set_title(UTF8ToUTF16(typed_url.title()));
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new_url.set_visit_count(old_url.visit_count());
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new_url.set_typed_count(typed_url.typed_count());
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new_url.set_last_visit(old_url.last_visit());
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new_url.set_hidden(typed_url.hidden());
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      updated_urls.push_back(
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        std::pair<history::URLID, history::URLRow>(old_url.id(), new_url));
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (old_url.title().compare(new_url.title()) != 0) {
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        titles.push_back(std::pair<GURL, string16>(new_url.url(),
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                   new_url.title()));
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      std::vector<base::Time> added_visits;
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      history::VisitVector removed_visits;
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      TypedUrlModelAssociator::DiffVisits(visits, typed_url,
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          &added_visits, &removed_visits);
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (added_visits.size()) {
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        new_visits.push_back(
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          std::pair<GURL, std::vector<base::Time> >(url, added_visits));
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (removed_visits.size()) {
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        deleted_visits.insert(deleted_visits.end(), removed_visits.begin(),
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              removed_visits.end());
302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!model_associator_->WriteToHistoryBackend(&titles, &new_urls,
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                &updated_urls,
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                &new_visits, &deleted_visits)) {
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    error_handler()->OnUnrecoverableError(FROM_HERE,
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        "Could not write to the history backend.");
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StartObserving();
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TypedUrlChangeProcessor::StartImpl(Profile* profile) {
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(expected_loop_ == MessageLoop::current());
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  observing_ = true;
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TypedUrlChangeProcessor::StopImpl() {
322731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  observing_ = false;
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TypedUrlChangeProcessor::StartObserving() {
328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(expected_loop_ == MessageLoop::current());
329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  notification_registrar_.Add(this,
330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              NotificationType::HISTORY_TYPED_URLS_MODIFIED,
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              NotificationService::AllSources());
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  notification_registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED,
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              NotificationService::AllSources());
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  notification_registrar_.Add(this, NotificationType::HISTORY_URL_VISITED,
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              NotificationService::AllSources());
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TypedUrlChangeProcessor::StopObserving() {
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(expected_loop_ == MessageLoop::current());
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  notification_registrar_.Remove(this,
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                 NotificationType::HISTORY_TYPED_URLS_MODIFIED,
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                 NotificationService::AllSources());
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  notification_registrar_.Remove(this,
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                 NotificationType::HISTORY_URLS_DELETED,
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                 NotificationService::AllSources());
346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  notification_registrar_.Remove(this,
347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                 NotificationType::HISTORY_URL_VISITED,
348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                 NotificationService::AllSources());
349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace browser_sync
352