OLD | NEW |
| (Empty) |
1 // Copyright 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. | |
4 | |
5 #include "sync/engine/build_commit_command.h" | |
6 | |
7 #include <limits> | |
8 #include <set> | |
9 #include <string> | |
10 #include <vector> | |
11 | |
12 #include "base/strings/string_util.h" | |
13 #include "sync/engine/syncer_proto_util.h" | |
14 #include "sync/internal_api/public/base/unique_position.h" | |
15 #include "sync/protocol/bookmark_specifics.pb.h" | |
16 #include "sync/protocol/sync.pb.h" | |
17 #include "sync/sessions/ordered_commit_set.h" | |
18 #include "sync/sessions/sync_session.h" | |
19 #include "sync/syncable/directory.h" | |
20 #include "sync/syncable/entry.h" | |
21 #include "sync/syncable/syncable_base_transaction.h" | |
22 #include "sync/syncable/syncable_changes_version.h" | |
23 #include "sync/syncable/syncable_proto_util.h" | |
24 #include "sync/util/time.h" | |
25 | |
26 using std::set; | |
27 using std::string; | |
28 using std::vector; | |
29 | |
30 namespace syncer { | |
31 | |
32 using sessions::SyncSession; | |
33 using syncable::Entry; | |
34 using syncable::IS_DEL; | |
35 using syncable::IS_UNAPPLIED_UPDATE; | |
36 using syncable::IS_UNSYNCED; | |
37 using syncable::Id; | |
38 using syncable::SPECIFICS; | |
39 using syncable::UNIQUE_POSITION; | |
40 | |
41 BuildCommitCommand::BuildCommitCommand( | |
42 syncable::BaseTransaction* trans, | |
43 const sessions::OrderedCommitSet& batch_commit_set, | |
44 sync_pb::ClientToServerMessage* commit_message, | |
45 ExtensionsActivity::Records* extensions_activity_buffer) | |
46 : trans_(trans), | |
47 batch_commit_set_(batch_commit_set), | |
48 commit_message_(commit_message), | |
49 extensions_activity_buffer_(extensions_activity_buffer) { | |
50 } | |
51 | |
52 BuildCommitCommand::~BuildCommitCommand() {} | |
53 | |
54 void BuildCommitCommand::AddExtensionsActivityToMessage( | |
55 SyncSession* session, sync_pb::CommitMessage* message) { | |
56 // We only send ExtensionsActivity to the server if bookmarks are being | |
57 // committed. | |
58 ExtensionsActivity* activity = session->context()->extensions_activity(); | |
59 if (batch_commit_set_.HasBookmarkCommitId()) { | |
60 // This isn't perfect, since the set of extensions activity may not | |
61 // correlate exactly with the items being committed. That's OK as | |
62 // long as we're looking for a rough estimate of extensions activity, | |
63 // not an precise mapping of which commits were triggered by which | |
64 // extension. | |
65 // | |
66 // We will push this list of extensions activity back into the | |
67 // ExtensionsActivityMonitor if this commit fails. That's why we must keep | |
68 // a copy of these records in the session. | |
69 activity->GetAndClearRecords(extensions_activity_buffer_); | |
70 | |
71 const ExtensionsActivity::Records& records = | |
72 *extensions_activity_buffer_; | |
73 for (ExtensionsActivity::Records::const_iterator it = | |
74 records.begin(); | |
75 it != records.end(); ++it) { | |
76 sync_pb::ChromiumExtensionsActivity* activity_message = | |
77 message->add_extensions_activity(); | |
78 activity_message->set_extension_id(it->second.extension_id); | |
79 activity_message->set_bookmark_writes_since_last_commit( | |
80 it->second.bookmark_write_count); | |
81 } | |
82 } | |
83 } | |
84 | |
85 void BuildCommitCommand::AddClientConfigParamsToMessage( | |
86 SyncSession* session, sync_pb::CommitMessage* message) { | |
87 const ModelSafeRoutingInfo& routing_info = session->context()->routing_info(); | |
88 sync_pb::ClientConfigParams* config_params = message->mutable_config_params(); | |
89 for (std::map<ModelType, ModelSafeGroup>::const_iterator iter = | |
90 routing_info.begin(); iter != routing_info.end(); ++iter) { | |
91 if (ProxyTypes().Has(iter->first)) | |
92 continue; | |
93 int field_number = GetSpecificsFieldNumberFromModelType(iter->first); | |
94 config_params->mutable_enabled_type_ids()->Add(field_number); | |
95 } | |
96 config_params->set_tabs_datatype_enabled( | |
97 routing_info.count(syncer::PROXY_TABS) > 0); | |
98 } | |
99 | |
100 namespace { | |
101 void SetEntrySpecifics(const Entry& meta_entry, | |
102 sync_pb::SyncEntity* sync_entry) { | |
103 // Add the new style extension and the folder bit. | |
104 sync_entry->mutable_specifics()->CopyFrom(meta_entry.GetSpecifics()); | |
105 sync_entry->set_folder(meta_entry.GetIsDir()); | |
106 | |
107 CHECK(!sync_entry->specifics().password().has_client_only_encrypted_data()); | |
108 DCHECK_EQ(meta_entry.GetModelType(), GetModelType(*sync_entry)); | |
109 } | |
110 } // namespace | |
111 | |
112 SyncerError BuildCommitCommand::ExecuteImpl(SyncSession* session) { | |
113 commit_message_->set_share(session->context()->account_name()); | |
114 commit_message_->set_message_contents(sync_pb::ClientToServerMessage::COMMIT); | |
115 | |
116 sync_pb::CommitMessage* commit_message = commit_message_->mutable_commit(); | |
117 commit_message->set_cache_guid(trans_->directory()->cache_guid()); | |
118 AddExtensionsActivityToMessage(session, commit_message); | |
119 AddClientConfigParamsToMessage(session, commit_message); | |
120 | |
121 for (size_t i = 0; i < batch_commit_set_.Size(); i++) { | |
122 int64 handle = batch_commit_set_.GetCommitHandleAt(i); | |
123 sync_pb::SyncEntity* sync_entry = commit_message->add_entries(); | |
124 | |
125 Entry meta_entry(trans_, syncable::GET_BY_HANDLE, handle); | |
126 CHECK(meta_entry.good()); | |
127 | |
128 DCHECK_NE(0UL, | |
129 session->context()->routing_info().count( | |
130 meta_entry.GetModelType())) | |
131 << "Committing change to datatype that's not actively enabled."; | |
132 | |
133 BuildCommitItem(meta_entry, sync_entry); | |
134 } | |
135 | |
136 | |
137 return SYNCER_OK; | |
138 } | |
139 | |
140 // static. | |
141 void BuildCommitCommand::BuildCommitItem( | |
142 const syncable::Entry& meta_entry, | |
143 sync_pb::SyncEntity* sync_entry) { | |
144 syncable::Id id = meta_entry.GetId(); | |
145 sync_entry->set_id_string(SyncableIdToProto(id)); | |
146 | |
147 string name = meta_entry.GetNonUniqueName(); | |
148 CHECK(!name.empty()); // Make sure this isn't an update. | |
149 // Note: Truncation is also performed in WriteNode::SetTitle(..). But this | |
150 // call is still necessary to handle any title changes that might originate | |
151 // elsewhere, or already be persisted in the directory. | |
152 TruncateUTF8ToByteSize(name, 255, &name); | |
153 sync_entry->set_name(name); | |
154 | |
155 // Set the non_unique_name. If we do, the server ignores | |
156 // the |name| value (using |non_unique_name| instead), and will return | |
157 // in the CommitResponse a unique name if one is generated. | |
158 // We send both because it may aid in logging. | |
159 sync_entry->set_non_unique_name(name); | |
160 | |
161 if (!meta_entry.GetUniqueClientTag().empty()) { | |
162 sync_entry->set_client_defined_unique_tag( | |
163 meta_entry.GetUniqueClientTag()); | |
164 } | |
165 | |
166 // Deleted items with server-unknown parent ids can be a problem so we set | |
167 // the parent to 0. (TODO(sync): Still true in protocol?). | |
168 Id new_parent_id; | |
169 if (meta_entry.GetIsDel() && | |
170 !meta_entry.GetParentId().ServerKnows()) { | |
171 new_parent_id = syncable::BaseTransaction::root_id(); | |
172 } else { | |
173 new_parent_id = meta_entry.GetParentId(); | |
174 } | |
175 sync_entry->set_parent_id_string(SyncableIdToProto(new_parent_id)); | |
176 | |
177 // If our parent has changed, send up the old one so the server | |
178 // can correctly deal with multiple parents. | |
179 // TODO(nick): With the server keeping track of the primary sync parent, | |
180 // it should not be necessary to provide the old_parent_id: the version | |
181 // number should suffice. | |
182 if (new_parent_id != meta_entry.GetServerParentId() && | |
183 0 != meta_entry.GetBaseVersion() && | |
184 syncable::CHANGES_VERSION != meta_entry.GetBaseVersion()) { | |
185 sync_entry->set_old_parent_id( | |
186 SyncableIdToProto(meta_entry.GetServerParentId())); | |
187 } | |
188 | |
189 int64 version = meta_entry.GetBaseVersion(); | |
190 if (syncable::CHANGES_VERSION == version || 0 == version) { | |
191 // Undeletions are only supported for items that have a client tag. | |
192 DCHECK(!id.ServerKnows() || | |
193 !meta_entry.GetUniqueClientTag().empty()) | |
194 << meta_entry; | |
195 | |
196 // Version 0 means to create or undelete an object. | |
197 sync_entry->set_version(0); | |
198 } else { | |
199 DCHECK(id.ServerKnows()) << meta_entry; | |
200 sync_entry->set_version(meta_entry.GetBaseVersion()); | |
201 } | |
202 sync_entry->set_ctime(TimeToProtoTime(meta_entry.GetCtime())); | |
203 sync_entry->set_mtime(TimeToProtoTime(meta_entry.GetMtime())); | |
204 | |
205 // Deletion is final on the server, let's move things and then delete them. | |
206 if (meta_entry.GetIsDel()) { | |
207 sync_entry->set_deleted(true); | |
208 } else { | |
209 if (meta_entry.GetSpecifics().has_bookmark()) { | |
210 // Both insert_after_item_id and position_in_parent fields are set only | |
211 // for legacy reasons. See comments in sync.proto for more information. | |
212 const Id& prev_id = meta_entry.GetPredecessorId(); | |
213 string prev_id_string = | |
214 prev_id.IsRoot() ? string() : prev_id.GetServerId(); | |
215 sync_entry->set_insert_after_item_id(prev_id_string); | |
216 sync_entry->set_position_in_parent( | |
217 meta_entry.GetUniquePosition().ToInt64()); | |
218 meta_entry.GetUniquePosition().ToProto( | |
219 sync_entry->mutable_unique_position()); | |
220 } | |
221 SetEntrySpecifics(meta_entry, sync_entry); | |
222 } | |
223 } | |
224 | |
225 } // namespace syncer | |
OLD | NEW |