1// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/sync/sessions/status_controller.h"
6
7#include "base/basictypes.h"
8#include "chrome/browser/sync/syncable/model_type.h"
9
10namespace browser_sync {
11namespace sessions {
12
13using syncable::FIRST_REAL_MODEL_TYPE;
14using syncable::MODEL_TYPE_COUNT;
15
16StatusController::StatusController(const ModelSafeRoutingInfo& routes)
17    : shared_(&is_dirty_),
18      per_model_group_deleter_(&per_model_group_),
19      is_dirty_(false),
20      group_restriction_in_effect_(false),
21      group_restriction_(GROUP_PASSIVE),
22      routing_info_(routes) {
23}
24
25StatusController::~StatusController() {}
26
27bool StatusController::TestAndClearIsDirty() {
28  bool is_dirty = is_dirty_;
29  is_dirty_ = false;
30  return is_dirty;
31}
32
33PerModelSafeGroupState* StatusController::GetOrCreateModelSafeGroupState(
34    bool restrict, ModelSafeGroup group) {
35  DCHECK(restrict == group_restriction_in_effect_) << "Group violation!";
36  if (per_model_group_.find(group) == per_model_group_.end()) {
37    PerModelSafeGroupState* state = new PerModelSafeGroupState(&is_dirty_);
38    per_model_group_[group] = state;
39    return state;
40  }
41  return per_model_group_[group];
42}
43
44void StatusController::increment_num_conflicting_commits_by(int value) {
45  if (value == 0)
46    return;
47  shared_.error_counters.mutate()->num_conflicting_commits += value;
48}
49
50void StatusController::increment_num_updates_downloaded_by(int value) {
51  shared_.syncer_status.mutate()->num_updates_downloaded_total += value;
52}
53
54void StatusController::set_types_needing_local_migration(
55    const syncable::ModelTypeSet& types) {
56  shared_.syncer_status.mutate()->types_needing_local_migration = types;
57}
58
59void StatusController::increment_num_tombstone_updates_downloaded_by(
60    int value) {
61  shared_.syncer_status.mutate()->num_tombstone_updates_downloaded_total +=
62      value;
63}
64
65void StatusController::reset_num_conflicting_commits() {
66  if (shared_.error_counters.value().num_conflicting_commits != 0)
67    shared_.error_counters.mutate()->num_conflicting_commits = 0;
68}
69
70void StatusController::set_num_consecutive_transient_error_commits(int value) {
71  if (shared_.error_counters.value().consecutive_transient_error_commits !=
72      value) {
73    shared_.error_counters.mutate()->consecutive_transient_error_commits =
74        value;
75  }
76}
77
78void StatusController::increment_num_consecutive_transient_error_commits_by(
79    int value) {
80  set_num_consecutive_transient_error_commits(
81      shared_.error_counters.value().consecutive_transient_error_commits +
82      value);
83}
84
85void StatusController::set_num_consecutive_errors(int value) {
86  if (shared_.error_counters.value().consecutive_errors != value)
87    shared_.error_counters.mutate()->consecutive_errors = value;
88}
89
90void StatusController::set_num_server_changes_remaining(
91    int64 changes_remaining) {
92  if (shared_.num_server_changes_remaining.value() != changes_remaining)
93    *(shared_.num_server_changes_remaining.mutate()) = changes_remaining;
94}
95
96void StatusController::set_invalid_store(bool invalid_store) {
97  if (shared_.syncer_status.value().invalid_store != invalid_store)
98    shared_.syncer_status.mutate()->invalid_store = invalid_store;
99}
100
101void StatusController::set_syncer_stuck(bool syncer_stuck) {
102  if (shared_.syncer_status.value().syncer_stuck != syncer_stuck)
103    shared_.syncer_status.mutate()->syncer_stuck = syncer_stuck;
104}
105
106void StatusController::set_syncing(bool syncing) {
107  if (shared_.syncer_status.value().syncing != syncing)
108    shared_.syncer_status.mutate()->syncing = syncing;
109}
110
111void StatusController::set_num_successful_bookmark_commits(int value) {
112  if (shared_.syncer_status.value().num_successful_bookmark_commits != value)
113    shared_.syncer_status.mutate()->num_successful_bookmark_commits = value;
114}
115
116void StatusController::set_unsynced_handles(
117    const std::vector<int64>& unsynced_handles) {
118  if (!operator==(unsynced_handles, shared_.unsynced_handles.value())) {
119    *(shared_.unsynced_handles.mutate()) = unsynced_handles;
120  }
121}
122
123void StatusController::increment_num_consecutive_errors() {
124  set_num_consecutive_errors(
125      shared_.error_counters.value().consecutive_errors + 1);
126}
127
128void StatusController::increment_num_consecutive_errors_by(int value) {
129  set_num_consecutive_errors(
130      shared_.error_counters.value().consecutive_errors + value);
131}
132
133void StatusController::increment_num_successful_bookmark_commits() {
134  set_num_successful_bookmark_commits(
135      shared_.syncer_status.value().num_successful_bookmark_commits + 1);
136}
137
138void StatusController::increment_num_successful_commits() {
139  shared_.syncer_status.mutate()->num_successful_commits++;
140}
141
142void StatusController::set_commit_set(const OrderedCommitSet& commit_set) {
143  DCHECK(!group_restriction_in_effect_);
144  shared_.commit_set = commit_set;
145}
146
147void StatusController::update_conflict_sets_built(bool built) {
148  shared_.control_params.conflict_sets_built |= built;
149}
150void StatusController::update_conflicts_resolved(bool resolved) {
151  shared_.control_params.conflict_sets_built |= resolved;
152}
153void StatusController::reset_conflicts_resolved() {
154  shared_.control_params.conflicts_resolved = false;
155}
156void StatusController::set_items_committed() {
157  shared_.control_params.items_committed = true;
158}
159
160// Returns the number of updates received from the sync server.
161int64 StatusController::CountUpdates() const {
162  const ClientToServerResponse& updates = shared_.updates_response;
163  if (updates.has_get_updates()) {
164    return updates.get_updates().entries().size();
165  } else {
166    return 0;
167  }
168}
169
170bool StatusController::CurrentCommitIdProjectionHasIndex(size_t index) {
171  OrderedCommitSet::Projection proj =
172      shared_.commit_set.GetCommitIdProjection(group_restriction_);
173  return std::binary_search(proj.begin(), proj.end(), index);
174}
175
176bool StatusController::HasConflictingUpdates() const {
177  DCHECK(!group_restriction_in_effect_)
178      << "HasConflictingUpdates applies to all ModelSafeGroups";
179  std::map<ModelSafeGroup, PerModelSafeGroupState*>::const_iterator it =
180    per_model_group_.begin();
181  for (; it != per_model_group_.end(); ++it) {
182    if (it->second->update_progress.HasConflictingUpdates())
183      return true;
184  }
185  return false;
186}
187
188int StatusController::TotalNumConflictingItems() const {
189  DCHECK(!group_restriction_in_effect_)
190      << "TotalNumConflictingItems applies to all ModelSafeGroups";
191  std::map<ModelSafeGroup, PerModelSafeGroupState*>::const_iterator it =
192    per_model_group_.begin();
193  int sum = 0;
194  for (; it != per_model_group_.end(); ++it) {
195    sum += it->second->conflict_progress.ConflictingItemsSize();
196  }
197  return sum;
198}
199
200bool StatusController::ServerSaysNothingMoreToDownload() const {
201  if (!download_updates_succeeded())
202    return false;
203
204  if (!updates_response().get_updates().has_changes_remaining()) {
205    NOTREACHED();  // Server should always send changes remaining.
206    return false;  // Avoid looping forever.
207  }
208  // Changes remaining is an estimate, but if it's estimated to be
209  // zero, that's firm and we don't have to ask again.
210  return updates_response().get_updates().changes_remaining() == 0;
211}
212
213}  // namespace sessions
214}  // namespace browser_sync
215