More bring up of ui code on iOS.
[chromium-blink-merge.git] / sync / engine / build_commit_command.cc
blob8e51ddb530a0e8a49072c5157285bf08413f2468
1 // Copyright (c) 2012 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.
5 #include "sync/engine/build_commit_command.h"
7 #include <limits>
8 #include <set>
9 #include <string>
10 #include <vector>
12 #include "base/string_util.h"
13 #include "sync/engine/syncer_proto_util.h"
14 #include "sync/protocol/bookmark_specifics.pb.h"
15 #include "sync/protocol/sync.pb.h"
16 #include "sync/sessions/ordered_commit_set.h"
17 #include "sync/sessions/sync_session.h"
18 #include "sync/syncable/directory.h"
19 #include "sync/syncable/mutable_entry.h"
20 #include "sync/syncable/syncable_changes_version.h"
21 #include "sync/syncable/syncable_proto_util.h"
22 #include "sync/syncable/write_transaction.h"
23 #include "sync/util/time.h"
25 using std::set;
26 using std::string;
27 using std::vector;
29 namespace syncer {
31 using sessions::SyncSession;
32 using syncable::Entry;
33 using syncable::IS_DEL;
34 using syncable::SERVER_POSITION_IN_PARENT;
35 using syncable::IS_UNAPPLIED_UPDATE;
36 using syncable::IS_UNSYNCED;
37 using syncable::Id;
38 using syncable::MutableEntry;
39 using syncable::SPECIFICS;
41 // static
42 int64 BuildCommitCommand::GetFirstPosition() {
43 return std::numeric_limits<int64>::min();
46 // static
47 int64 BuildCommitCommand::GetLastPosition() {
48 return std::numeric_limits<int64>::max();
51 // static
52 int64 BuildCommitCommand::GetGap() {
53 return 1LL << 20;
56 BuildCommitCommand::BuildCommitCommand(
57 const sessions::OrderedCommitSet& batch_commit_set,
58 sync_pb::ClientToServerMessage* commit_message)
59 : batch_commit_set_(batch_commit_set), commit_message_(commit_message) {
62 BuildCommitCommand::~BuildCommitCommand() {}
64 void BuildCommitCommand::AddExtensionsActivityToMessage(
65 SyncSession* session, sync_pb::CommitMessage* message) {
66 // We only send ExtensionsActivity to the server if bookmarks are being
67 // committed.
68 ExtensionsActivityMonitor* monitor = session->context()->extensions_monitor();
69 if (batch_commit_set_.HasBookmarkCommitId()) {
70 // This isn't perfect, since the set of extensions activity may not
71 // correlate exactly with the items being committed. That's OK as
72 // long as we're looking for a rough estimate of extensions activity,
73 // not an precise mapping of which commits were triggered by which
74 // extension.
76 // We will push this list of extensions activity back into the
77 // ExtensionsActivityMonitor if this commit fails. That's why we must keep
78 // a copy of these records in the session.
79 monitor->GetAndClearRecords(session->mutable_extensions_activity());
81 const ExtensionsActivityMonitor::Records& records =
82 session->extensions_activity();
83 for (ExtensionsActivityMonitor::Records::const_iterator it =
84 records.begin();
85 it != records.end(); ++it) {
86 sync_pb::ChromiumExtensionsActivity* activity_message =
87 message->add_extensions_activity();
88 activity_message->set_extension_id(it->second.extension_id);
89 activity_message->set_bookmark_writes_since_last_commit(
90 it->second.bookmark_write_count);
95 namespace {
96 void SetEntrySpecifics(MutableEntry* meta_entry,
97 sync_pb::SyncEntity* sync_entry) {
98 // Add the new style extension and the folder bit.
99 sync_entry->mutable_specifics()->CopyFrom(meta_entry->Get(SPECIFICS));
100 sync_entry->set_folder(meta_entry->Get(syncable::IS_DIR));
102 DCHECK_EQ(meta_entry->GetModelType(), GetModelType(*sync_entry));
104 } // namespace
106 SyncerError BuildCommitCommand::ExecuteImpl(SyncSession* session) {
107 commit_message_->set_share(session->context()->account_name());
108 commit_message_->set_message_contents(sync_pb::ClientToServerMessage::COMMIT);
110 sync_pb::CommitMessage* commit_message = commit_message_->mutable_commit();
111 commit_message->set_cache_guid(
112 session->write_transaction()->directory()->cache_guid());
113 AddExtensionsActivityToMessage(session, commit_message);
115 // Cache previously computed position values. Because |commit_ids|
116 // is already in sibling order, we should always hit this map after
117 // the first sibling in a consecutive run of commit items. The
118 // entries in this map are (low, high) values describing the
119 // space of positions that are immediate successors of the item
120 // whose ID is the map's key.
121 std::map<Id, std::pair<int64, int64> > position_map;
123 for (size_t i = 0; i < batch_commit_set_.Size(); i++) {
124 Id id = batch_commit_set_.GetCommitIdAt(i);
125 sync_pb::SyncEntity* sync_entry = commit_message->add_entries();
126 sync_entry->set_id_string(SyncableIdToProto(id));
127 MutableEntry meta_entry(session->write_transaction(),
128 syncable::GET_BY_ID, id);
129 CHECK(meta_entry.good());
131 DCHECK(0 != session->routing_info().count(meta_entry.GetModelType()))
132 << "Committing change to datatype that's not actively enabled.";
134 string name = meta_entry.Get(syncable::NON_UNIQUE_NAME);
135 CHECK(!name.empty()); // Make sure this isn't an update.
136 TruncateUTF8ToByteSize(name, 255, &name);
137 sync_entry->set_name(name);
139 // Set the non_unique_name. If we do, the server ignores
140 // the |name| value (using |non_unique_name| instead), and will return
141 // in the CommitResponse a unique name if one is generated.
142 // We send both because it may aid in logging.
143 sync_entry->set_non_unique_name(name);
145 if (!meta_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) {
146 sync_entry->set_client_defined_unique_tag(
147 meta_entry.Get(syncable::UNIQUE_CLIENT_TAG));
150 // Deleted items with server-unknown parent ids can be a problem so we set
151 // the parent to 0. (TODO(sync): Still true in protocol?).
152 Id new_parent_id;
153 if (meta_entry.Get(syncable::IS_DEL) &&
154 !meta_entry.Get(syncable::PARENT_ID).ServerKnows()) {
155 new_parent_id = session->write_transaction()->root_id();
156 } else {
157 new_parent_id = meta_entry.Get(syncable::PARENT_ID);
159 sync_entry->set_parent_id_string(SyncableIdToProto(new_parent_id));
161 // If our parent has changed, send up the old one so the server
162 // can correctly deal with multiple parents.
163 // TODO(nick): With the server keeping track of the primary sync parent,
164 // it should not be necessary to provide the old_parent_id: the version
165 // number should suffice.
166 if (new_parent_id != meta_entry.Get(syncable::SERVER_PARENT_ID) &&
167 0 != meta_entry.Get(syncable::BASE_VERSION) &&
168 syncable::CHANGES_VERSION != meta_entry.Get(syncable::BASE_VERSION)) {
169 sync_entry->set_old_parent_id(
170 SyncableIdToProto(meta_entry.Get(syncable::SERVER_PARENT_ID)));
173 int64 version = meta_entry.Get(syncable::BASE_VERSION);
174 if (syncable::CHANGES_VERSION == version || 0 == version) {
175 // Undeletions are only supported for items that have a client tag.
176 DCHECK(!id.ServerKnows() ||
177 !meta_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty())
178 << meta_entry;
180 // Version 0 means to create or undelete an object.
181 sync_entry->set_version(0);
182 } else {
183 DCHECK(id.ServerKnows()) << meta_entry;
184 sync_entry->set_version(meta_entry.Get(syncable::BASE_VERSION));
186 sync_entry->set_ctime(TimeToProtoTime(meta_entry.Get(syncable::CTIME)));
187 sync_entry->set_mtime(TimeToProtoTime(meta_entry.Get(syncable::MTIME)));
189 // Deletion is final on the server, let's move things and then delete them.
190 if (meta_entry.Get(IS_DEL)) {
191 sync_entry->set_deleted(true);
192 } else {
193 if (meta_entry.Get(SPECIFICS).has_bookmark()) {
194 // Common data in both new and old protocol.
195 const Id& prev_id = meta_entry.Get(syncable::PREV_ID);
196 string prev_id_string =
197 prev_id.IsRoot() ? string() : prev_id.GetServerId();
198 sync_entry->set_insert_after_item_id(prev_id_string);
200 // Compute a numeric position based on what we know locally.
201 std::pair<int64, int64> position_block(
202 GetFirstPosition(), GetLastPosition());
203 std::map<Id, std::pair<int64, int64> >::iterator prev_pos =
204 position_map.find(prev_id);
205 if (prev_pos != position_map.end()) {
206 position_block = prev_pos->second;
207 position_map.erase(prev_pos);
208 } else {
209 position_block = std::make_pair(
210 FindAnchorPosition(syncable::PREV_ID, meta_entry),
211 FindAnchorPosition(syncable::NEXT_ID, meta_entry));
213 position_block.first = InterpolatePosition(position_block.first,
214 position_block.second);
216 position_map[id] = position_block;
217 sync_entry->set_position_in_parent(position_block.first);
219 SetEntrySpecifics(&meta_entry, sync_entry);
223 return SYNCER_OK;
226 int64 BuildCommitCommand::FindAnchorPosition(syncable::IdField direction,
227 const syncable::Entry& entry) {
228 Id next_id = entry.Get(direction);
229 while (!next_id.IsRoot()) {
230 Entry next_entry(entry.trans(),
231 syncable::GET_BY_ID,
232 next_id);
233 if (!next_entry.Get(IS_UNSYNCED) && !next_entry.Get(IS_UNAPPLIED_UPDATE)) {
234 return next_entry.Get(SERVER_POSITION_IN_PARENT);
236 next_id = next_entry.Get(direction);
238 return
239 direction == syncable::PREV_ID ?
240 GetFirstPosition() : GetLastPosition();
243 int64 BuildCommitCommand::InterpolatePosition(const int64 lo,
244 const int64 hi) {
245 DCHECK_LE(lo, hi);
247 // The first item to be added under a parent gets a position of zero.
248 if (lo == GetFirstPosition() && hi == GetLastPosition())
249 return 0;
251 // For small gaps, we do linear interpolation. For larger gaps,
252 // we use an additive offset of |GetGap()|. We are careful to avoid
253 // signed integer overflow.
254 uint64 delta = static_cast<uint64>(hi) - static_cast<uint64>(lo);
255 if (delta <= static_cast<uint64>(GetGap()*2))
256 return lo + (static_cast<int64>(delta) + 7) / 8; // Interpolate.
257 else if (lo == GetFirstPosition())
258 return hi - GetGap(); // Extend range just before successor.
259 else
260 return lo + GetGap(); // Use or extend range just after predecessor.
264 } // namespace syncer