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.
5 #include "sync/engine/build_commit_command.h"
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"
32 using sessions::SyncSession
;
33 using syncable::Entry
;
34 using syncable::IS_DEL
;
35 using syncable::IS_UNAPPLIED_UPDATE
;
36 using syncable::IS_UNSYNCED
;
38 using syncable::SPECIFICS
;
39 using syncable::UNIQUE_POSITION
;
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
)
47 batch_commit_set_(batch_commit_set
),
48 commit_message_(commit_message
),
49 extensions_activity_buffer_(extensions_activity_buffer
) {
52 BuildCommitCommand::~BuildCommitCommand() {}
54 void BuildCommitCommand::AddExtensionsActivityToMessage(
55 SyncSession
* session
, sync_pb::CommitMessage
* message
) {
56 // We only send ExtensionsActivity to the server if bookmarks are being
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
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_
);
71 const ExtensionsActivity::Records
& records
=
72 *extensions_activity_buffer_
;
73 for (ExtensionsActivity::Records::const_iterator it
=
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
);
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
))
93 int field_number
= GetSpecificsFieldNumberFromModelType(iter
->first
);
94 config_params
->mutable_enabled_type_ids()->Add(field_number
);
96 config_params
->set_tabs_datatype_enabled(
97 routing_info
.count(syncer::PROXY_TABS
) > 0);
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
.Get(SPECIFICS
));
105 sync_entry
->set_folder(meta_entry
.Get(syncable::IS_DIR
));
107 CHECK(!sync_entry
->specifics().password().has_client_only_encrypted_data());
108 DCHECK_EQ(meta_entry
.GetModelType(), GetModelType(*sync_entry
));
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
);
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
);
121 for (size_t i
= 0; i
< batch_commit_set_
.Size(); i
++) {
122 Id id
= batch_commit_set_
.GetCommitIdAt(i
);
123 sync_pb::SyncEntity
* sync_entry
= commit_message
->add_entries();
125 Entry
meta_entry(trans_
, syncable::GET_BY_ID
, id
);
126 CHECK(meta_entry
.good());
129 session
->context()->routing_info().count(
130 meta_entry
.GetModelType()))
131 << "Committing change to datatype that's not actively enabled.";
133 BuildCommitItem(meta_entry
, sync_entry
);
141 void BuildCommitCommand::BuildCommitItem(
142 const syncable::Entry
& meta_entry
,
143 sync_pb::SyncEntity
* sync_entry
) {
144 syncable::Id id
= meta_entry
.Get(syncable::ID
);
145 sync_entry
->set_id_string(SyncableIdToProto(id
));
147 string name
= meta_entry
.Get(syncable::NON_UNIQUE_NAME
);
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
);
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
);
161 if (!meta_entry
.Get(syncable::UNIQUE_CLIENT_TAG
).empty()) {
162 sync_entry
->set_client_defined_unique_tag(
163 meta_entry
.Get(syncable::UNIQUE_CLIENT_TAG
));
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?).
169 if (meta_entry
.Get(syncable::IS_DEL
) &&
170 !meta_entry
.Get(syncable::PARENT_ID
).ServerKnows()) {
171 new_parent_id
= syncable::BaseTransaction::root_id();
173 new_parent_id
= meta_entry
.Get(syncable::PARENT_ID
);
175 sync_entry
->set_parent_id_string(SyncableIdToProto(new_parent_id
));
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
.Get(syncable::SERVER_PARENT_ID
) &&
183 0 != meta_entry
.Get(syncable::BASE_VERSION
) &&
184 syncable::CHANGES_VERSION
!= meta_entry
.Get(syncable::BASE_VERSION
)) {
185 sync_entry
->set_old_parent_id(
186 SyncableIdToProto(meta_entry
.Get(syncable::SERVER_PARENT_ID
)));
189 int64 version
= meta_entry
.Get(syncable::BASE_VERSION
);
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
.Get(syncable::UNIQUE_CLIENT_TAG
).empty())
196 // Version 0 means to create or undelete an object.
197 sync_entry
->set_version(0);
199 DCHECK(id
.ServerKnows()) << meta_entry
;
200 sync_entry
->set_version(meta_entry
.Get(syncable::BASE_VERSION
));
202 sync_entry
->set_ctime(TimeToProtoTime(meta_entry
.Get(syncable::CTIME
)));
203 sync_entry
->set_mtime(TimeToProtoTime(meta_entry
.Get(syncable::MTIME
)));
205 // Deletion is final on the server, let's move things and then delete them.
206 if (meta_entry
.Get(IS_DEL
)) {
207 sync_entry
->set_deleted(true);
209 if (meta_entry
.Get(SPECIFICS
).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
.Get(UNIQUE_POSITION
).ToInt64());
218 meta_entry
.Get(UNIQUE_POSITION
).ToProto(
219 sync_entry
->mutable_unique_position());
221 SetEntrySpecifics(meta_entry
, sync_entry
);
225 } // namespace syncer