Add a string for translation.
[chromium-blink-merge.git] / components / sync_driver / generic_change_processor.cc
blobadc4c90ed42d96897330b9168588cea05beb58b3
1 // Copyright 2014 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 "components/sync_driver/generic_change_processor.h"
7 #include "base/location.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "sync/api/sync_change.h"
11 #include "sync/api/sync_error.h"
12 #include "sync/api/syncable_service.h"
13 #include "sync/internal_api/public/base_node.h"
14 #include "sync/internal_api/public/change_record.h"
15 #include "sync/internal_api/public/read_node.h"
16 #include "sync/internal_api/public/read_transaction.h"
17 #include "sync/internal_api/public/util/unrecoverable_error_handler.h"
18 #include "sync/internal_api/public/write_node.h"
19 #include "sync/internal_api/public/write_transaction.h"
20 #include "sync/syncable/entry.h" // TODO(tim): Bug 123674.
22 namespace browser_sync {
24 namespace {
26 const int kContextSizeLimit = 1024; // Datatype context size limit.
28 void SetNodeSpecifics(const sync_pb::EntitySpecifics& entity_specifics,
29 syncer::WriteNode* write_node) {
30 if (syncer::GetModelTypeFromSpecifics(entity_specifics) ==
31 syncer::PASSWORDS) {
32 write_node->SetPasswordSpecifics(
33 entity_specifics.password().client_only_encrypted_data());
34 } else {
35 write_node->SetEntitySpecifics(entity_specifics);
39 syncer::SyncData BuildRemoteSyncData(
40 int64 sync_id,
41 const syncer::BaseNode& read_node,
42 const syncer::AttachmentServiceProxy& attachment_service_proxy) {
43 const syncer::AttachmentIdList& attachment_ids = read_node.GetAttachmentIds();
44 // Use the specifics of non-password datatypes directly (encryption has
45 // already been handled).
46 if (read_node.GetModelType() != syncer::PASSWORDS) {
47 return syncer::SyncData::CreateRemoteData(sync_id,
48 read_node.GetEntitySpecifics(),
49 read_node.GetModificationTime(),
50 attachment_ids,
51 attachment_service_proxy);
54 // Passwords must be accessed differently, to account for their encryption,
55 // and stored into a temporary EntitySpecifics.
56 sync_pb::EntitySpecifics password_holder;
57 password_holder.mutable_password()->mutable_client_only_encrypted_data()->
58 CopyFrom(read_node.GetPasswordSpecifics());
59 return syncer::SyncData::CreateRemoteData(sync_id,
60 password_holder,
61 read_node.GetModificationTime(),
62 attachment_ids,
63 attachment_service_proxy);
66 } // namespace
68 GenericChangeProcessor::GenericChangeProcessor(
69 DataTypeErrorHandler* error_handler,
70 const base::WeakPtr<syncer::SyncableService>& local_service,
71 const base::WeakPtr<syncer::SyncMergeResult>& merge_result,
72 syncer::UserShare* user_share,
73 scoped_ptr<syncer::AttachmentService> attachment_service)
74 : ChangeProcessor(error_handler),
75 local_service_(local_service),
76 merge_result_(merge_result),
77 share_handle_(user_share),
78 attachment_service_(attachment_service.Pass()),
79 attachment_service_weak_ptr_factory_(attachment_service_.get()),
80 attachment_service_proxy_(
81 base::MessageLoopProxy::current(),
82 attachment_service_weak_ptr_factory_.GetWeakPtr()) {
83 DCHECK(CalledOnValidThread());
84 DCHECK(attachment_service_);
87 GenericChangeProcessor::~GenericChangeProcessor() {
88 DCHECK(CalledOnValidThread());
91 void GenericChangeProcessor::ApplyChangesFromSyncModel(
92 const syncer::BaseTransaction* trans,
93 int64 model_version,
94 const syncer::ImmutableChangeRecordList& changes) {
95 DCHECK(CalledOnValidThread());
96 DCHECK(syncer_changes_.empty());
97 for (syncer::ChangeRecordList::const_iterator it =
98 changes.Get().begin(); it != changes.Get().end(); ++it) {
99 if (it->action == syncer::ChangeRecord::ACTION_DELETE) {
100 scoped_ptr<sync_pb::EntitySpecifics> specifics;
101 if (it->specifics.has_password()) {
102 DCHECK(it->extra.get());
103 specifics.reset(new sync_pb::EntitySpecifics(it->specifics));
104 specifics->mutable_password()->mutable_client_only_encrypted_data()->
105 CopyFrom(it->extra->unencrypted());
107 const syncer::AttachmentIdList empty_list_of_attachment_ids;
108 syncer_changes_.push_back(
109 syncer::SyncChange(FROM_HERE,
110 syncer::SyncChange::ACTION_DELETE,
111 syncer::SyncData::CreateRemoteData(
112 it->id,
113 specifics ? *specifics : it->specifics,
114 base::Time(),
115 empty_list_of_attachment_ids,
116 attachment_service_proxy_)));
117 } else {
118 syncer::SyncChange::SyncChangeType action =
119 (it->action == syncer::ChangeRecord::ACTION_ADD) ?
120 syncer::SyncChange::ACTION_ADD : syncer::SyncChange::ACTION_UPDATE;
121 // Need to load specifics from node.
122 syncer::ReadNode read_node(trans);
123 if (read_node.InitByIdLookup(it->id) != syncer::BaseNode::INIT_OK) {
124 error_handler()->OnSingleDatatypeUnrecoverableError(
125 FROM_HERE,
126 "Failed to look up data for received change with id " +
127 base::Int64ToString(it->id));
128 return;
130 syncer_changes_.push_back(syncer::SyncChange(
131 FROM_HERE,
132 action,
133 BuildRemoteSyncData(it->id, read_node, attachment_service_proxy_)));
138 void GenericChangeProcessor::CommitChangesFromSyncModel() {
139 DCHECK(CalledOnValidThread());
140 if (syncer_changes_.empty())
141 return;
142 if (!local_service_.get()) {
143 syncer::ModelType type = syncer_changes_[0].sync_data().GetDataType();
144 syncer::SyncError error(FROM_HERE,
145 syncer::SyncError::DATATYPE_ERROR,
146 "Local service destroyed.",
147 type);
148 error_handler()->OnSingleDatatypeUnrecoverableError(error.location(),
149 error.message());
150 return;
152 syncer::SyncError error = local_service_->ProcessSyncChanges(FROM_HERE,
153 syncer_changes_);
154 syncer_changes_.clear();
155 if (error.IsSet()) {
156 error_handler()->OnSingleDatatypeUnrecoverableError(
157 error.location(), error.message());
161 syncer::SyncDataList GenericChangeProcessor::GetAllSyncData(
162 syncer::ModelType type) const {
163 // This is slow / memory intensive. Should be used sparingly by datatypes.
164 syncer::SyncDataList data;
165 GetAllSyncDataReturnError(type, &data);
166 return data;
169 syncer::SyncError GenericChangeProcessor::UpdateDataTypeContext(
170 syncer::ModelType type,
171 syncer::SyncChangeProcessor::ContextRefreshStatus refresh_status,
172 const std::string& context) {
173 DCHECK(syncer::ProtocolTypes().Has(type));
175 if (context.size() > static_cast<size_t>(kContextSizeLimit)) {
176 return syncer::SyncError(FROM_HERE,
177 syncer::SyncError::DATATYPE_ERROR,
178 "Context size limit exceeded.",
179 type);
182 syncer::WriteTransaction trans(FROM_HERE, share_handle());
183 trans.SetDataTypeContext(type, refresh_status, context);
185 // TODO(zea): plumb a pointer to the PSS or SyncManagerImpl here so we can
186 // trigger a datatype nudge if |refresh_status == REFRESH_NEEDED|.
188 return syncer::SyncError();
191 syncer::SyncError GenericChangeProcessor::GetAllSyncDataReturnError(
192 syncer::ModelType type,
193 syncer::SyncDataList* current_sync_data) const {
194 DCHECK(CalledOnValidThread());
195 std::string type_name = syncer::ModelTypeToString(type);
196 syncer::ReadTransaction trans(FROM_HERE, share_handle());
197 syncer::ReadNode root(&trans);
198 if (root.InitByTagLookup(syncer::ModelTypeToRootTag(type)) !=
199 syncer::BaseNode::INIT_OK) {
200 syncer::SyncError error(FROM_HERE,
201 syncer::SyncError::DATATYPE_ERROR,
202 "Server did not create the top-level " + type_name +
203 " node. We might be running against an out-of-"
204 "date server.",
205 type);
206 return error;
209 // TODO(akalin): We'll have to do a tree traversal for bookmarks.
210 DCHECK_NE(type, syncer::BOOKMARKS);
212 std::vector<int64> child_ids;
213 root.GetChildIds(&child_ids);
215 for (std::vector<int64>::iterator it = child_ids.begin();
216 it != child_ids.end(); ++it) {
217 syncer::ReadNode sync_child_node(&trans);
218 if (sync_child_node.InitByIdLookup(*it) !=
219 syncer::BaseNode::INIT_OK) {
220 syncer::SyncError error(FROM_HERE,
221 syncer::SyncError::DATATYPE_ERROR,
222 "Failed to fetch child node for type " +
223 type_name + ".",
224 type);
225 return error;
227 current_sync_data->push_back(BuildRemoteSyncData(
228 sync_child_node.GetId(), sync_child_node, attachment_service_proxy_));
230 return syncer::SyncError();
233 bool GenericChangeProcessor::GetDataTypeContext(syncer::ModelType type,
234 std::string* context) const {
235 syncer::ReadTransaction trans(FROM_HERE, share_handle());
236 sync_pb::DataTypeContext context_proto;
237 trans.GetDataTypeContext(type, &context_proto);
238 if (!context_proto.has_context())
239 return false;
241 DCHECK_EQ(type,
242 syncer::GetModelTypeFromSpecificsFieldNumber(
243 context_proto.data_type_id()));
244 *context = context_proto.context();
245 return true;
248 int GenericChangeProcessor::GetSyncCountForType(syncer::ModelType type) {
249 syncer::ReadTransaction trans(FROM_HERE, share_handle());
250 syncer::ReadNode root(&trans);
251 if (root.InitByTagLookup(syncer::ModelTypeToRootTag(type)) !=
252 syncer::BaseNode::INIT_OK)
253 return 0;
255 // Subtract one to account for type's root node.
256 return root.GetTotalNodeCount() - 1;
259 namespace {
261 // TODO(isherman): Investigating http://crbug.com/121592
262 // WARNING: this code is sensitive to compiler optimizations. Be careful
263 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
264 // the compiler attempts to merge it with other calls, losing useful information
265 // in breakpad uploads.
266 syncer::SyncError LogLookupFailure(
267 syncer::BaseNode::InitByLookupResult lookup_result,
268 const tracked_objects::Location& from_here,
269 const std::string& error_prefix,
270 syncer::ModelType type,
271 DataTypeErrorHandler* error_handler) {
272 switch (lookup_result) {
273 case syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD: {
274 syncer::SyncError error;
275 error.Reset(from_here,
276 error_prefix +
277 "could not find entry matching the lookup criteria.",
278 type);
279 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
280 error.message());
281 LOG(ERROR) << "Delete: Bad entry.";
282 return error;
284 case syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL: {
285 syncer::SyncError error;
286 error.Reset(from_here, error_prefix + "entry is already deleted.", type);
287 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
288 error.message());
289 LOG(ERROR) << "Delete: Deleted entry.";
290 return error;
292 case syncer::BaseNode::INIT_FAILED_DECRYPT_IF_NECESSARY: {
293 syncer::SyncError error;
294 error.Reset(from_here, error_prefix + "unable to decrypt", type);
295 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
296 error.message());
297 LOG(ERROR) << "Delete: Undecryptable entry.";
298 return error;
300 case syncer::BaseNode::INIT_FAILED_PRECONDITION: {
301 syncer::SyncError error;
302 error.Reset(from_here,
303 error_prefix + "a precondition was not met for calling init.",
304 type);
305 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
306 error.message());
307 LOG(ERROR) << "Delete: Failed precondition.";
308 return error;
310 default: {
311 syncer::SyncError error;
312 // Should have listed all the possible error cases above.
313 error.Reset(from_here, error_prefix + "unknown error", type);
314 error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
315 error.message());
316 LOG(ERROR) << "Delete: Unknown error.";
317 return error;
322 syncer::SyncError AttemptDelete(
323 const syncer::SyncChange& change,
324 syncer::ModelType type,
325 const std::string& type_str,
326 syncer::WriteNode* node,
327 DataTypeErrorHandler* error_handler) {
328 DCHECK_EQ(change.change_type(), syncer::SyncChange::ACTION_DELETE);
329 if (change.sync_data().IsLocal()) {
330 const std::string& tag = syncer::SyncDataLocal(change.sync_data()).GetTag();
331 if (tag.empty()) {
332 syncer::SyncError error(
333 FROM_HERE,
334 syncer::SyncError::DATATYPE_ERROR,
335 "Failed to delete " + type_str + " node. Local data, empty tag. " +
336 change.location().ToString(),
337 type);
338 error_handler->OnSingleDatatypeUnrecoverableError(error.location(),
339 error.message());
340 NOTREACHED();
341 return error;
344 syncer::BaseNode::InitByLookupResult result =
345 node->InitByClientTagLookup(change.sync_data().GetDataType(), tag);
346 if (result != syncer::BaseNode::INIT_OK) {
347 return LogLookupFailure(
348 result, FROM_HERE,
349 "Failed to delete " + type_str + " node. Local data. " +
350 change.location().ToString(),
351 type, error_handler);
353 } else {
354 syncer::BaseNode::InitByLookupResult result = node->InitByIdLookup(
355 syncer::SyncDataRemote(change.sync_data()).GetId());
356 if (result != syncer::BaseNode::INIT_OK) {
357 return LogLookupFailure(
358 result, FROM_HERE,
359 "Failed to delete " + type_str + " node. Non-local data. " +
360 change.location().ToString(),
361 type, error_handler);
364 if (IsActOnceDataType(type))
365 node->Drop();
366 else
367 node->Tombstone();
368 return syncer::SyncError();
371 } // namespace
373 syncer::SyncError GenericChangeProcessor::ProcessSyncChanges(
374 const tracked_objects::Location& from_here,
375 const syncer::SyncChangeList& list_of_changes) {
376 DCHECK(CalledOnValidThread());
377 syncer::WriteTransaction trans(from_here, share_handle());
379 for (syncer::SyncChangeList::const_iterator iter = list_of_changes.begin();
380 iter != list_of_changes.end();
381 ++iter) {
382 const syncer::SyncChange& change = *iter;
383 DCHECK_NE(change.sync_data().GetDataType(), syncer::UNSPECIFIED);
384 syncer::ModelType type = change.sync_data().GetDataType();
385 std::string type_str = syncer::ModelTypeToString(type);
386 syncer::WriteNode sync_node(&trans);
387 if (change.change_type() == syncer::SyncChange::ACTION_DELETE) {
388 syncer::SyncError error =
389 AttemptDelete(change, type, type_str, &sync_node, error_handler());
390 if (error.IsSet()) {
391 NOTREACHED();
392 return error;
394 attachment_service_->OnSyncDataDelete(change.sync_data());
395 if (merge_result_.get()) {
396 merge_result_->set_num_items_deleted(
397 merge_result_->num_items_deleted() + 1);
399 } else if (change.change_type() == syncer::SyncChange::ACTION_ADD) {
400 syncer::SyncError error =
401 HandleActionAdd(change, type_str, type, trans, &sync_node);
402 if (error.IsSet()) {
403 return error;
405 } else if (change.change_type() == syncer::SyncChange::ACTION_UPDATE) {
406 syncer::SyncError error =
407 HandleActionUpdate(change, type_str, type, trans, &sync_node);
408 if (error.IsSet()) {
409 return error;
411 } else {
412 syncer::SyncError error(
413 FROM_HERE,
414 syncer::SyncError::DATATYPE_ERROR,
415 "Received unset SyncChange in the change processor, " +
416 change.location().ToString(),
417 type);
418 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
419 error.message());
420 NOTREACHED();
421 LOG(ERROR) << "Unset sync change.";
422 return error;
425 return syncer::SyncError();
428 // WARNING: this code is sensitive to compiler optimizations. Be careful
429 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
430 // the compiler attempts to merge it with other calls, losing useful information
431 // in breakpad uploads.
432 syncer::SyncError GenericChangeProcessor::HandleActionAdd(
433 const syncer::SyncChange& change,
434 const std::string& type_str,
435 const syncer::ModelType& type,
436 const syncer::WriteTransaction& trans,
437 syncer::WriteNode* sync_node) {
438 // TODO(sync): Handle other types of creation (custom parents, folders,
439 // etc.).
440 syncer::ReadNode root_node(&trans);
441 if (root_node.InitByTagLookup(syncer::ModelTypeToRootTag(
442 change.sync_data().GetDataType())) != syncer::BaseNode::INIT_OK) {
443 syncer::SyncError error(FROM_HERE,
444 syncer::SyncError::DATATYPE_ERROR,
445 "Failed to look up root node for type " + type_str,
446 type);
447 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
448 error.message());
449 NOTREACHED();
450 LOG(ERROR) << "Create: no root node.";
451 return error;
453 syncer::WriteNode::InitUniqueByCreationResult result =
454 sync_node->InitUniqueByCreation(
455 change.sync_data().GetDataType(),
456 root_node,
457 syncer::SyncDataLocal(change.sync_data()).GetTag());
458 if (result != syncer::WriteNode::INIT_SUCCESS) {
459 std::string error_prefix = "Failed to create " + type_str + " node: " +
460 change.location().ToString() + ", ";
461 switch (result) {
462 case syncer::WriteNode::INIT_FAILED_EMPTY_TAG: {
463 syncer::SyncError error;
464 error.Reset(FROM_HERE, error_prefix + "empty tag", type);
465 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
466 error.message());
467 LOG(ERROR) << "Create: Empty tag.";
468 return error;
470 case syncer::WriteNode::INIT_FAILED_ENTRY_ALREADY_EXISTS: {
471 syncer::SyncError error;
472 error.Reset(FROM_HERE, error_prefix + "entry already exists", type);
473 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
474 error.message());
475 LOG(ERROR) << "Create: Entry exists.";
476 return error;
478 case syncer::WriteNode::INIT_FAILED_COULD_NOT_CREATE_ENTRY: {
479 syncer::SyncError error;
480 error.Reset(FROM_HERE, error_prefix + "failed to create entry", type);
481 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
482 error.message());
483 LOG(ERROR) << "Create: Could not create entry.";
484 return error;
486 case syncer::WriteNode::INIT_FAILED_SET_PREDECESSOR: {
487 syncer::SyncError error;
488 error.Reset(
489 FROM_HERE, error_prefix + "failed to set predecessor", type);
490 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
491 error.message());
492 LOG(ERROR) << "Create: Bad predecessor.";
493 return error;
495 default: {
496 syncer::SyncError error;
497 error.Reset(FROM_HERE, error_prefix + "unknown error", type);
498 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
499 error.message());
500 LOG(ERROR) << "Create: Unknown error.";
501 return error;
505 sync_node->SetTitle(change.sync_data().GetTitle());
506 SetNodeSpecifics(change.sync_data().GetSpecifics(), sync_node);
507 attachment_service_->OnSyncDataAdd(change.sync_data());
508 if (merge_result_.get()) {
509 merge_result_->set_num_items_added(merge_result_->num_items_added() + 1);
511 return syncer::SyncError();
513 // WARNING: this code is sensitive to compiler optimizations. Be careful
514 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
515 // the compiler attempts to merge it with other calls, losing useful information
516 // in breakpad uploads.
517 syncer::SyncError GenericChangeProcessor::HandleActionUpdate(
518 const syncer::SyncChange& change,
519 const std::string& type_str,
520 const syncer::ModelType& type,
521 const syncer::WriteTransaction& trans,
522 syncer::WriteNode* sync_node) {
523 // TODO(zea): consider having this logic for all possible changes?
524 syncer::BaseNode::InitByLookupResult result =
525 sync_node->InitByClientTagLookup(
526 change.sync_data().GetDataType(),
527 syncer::SyncDataLocal(change.sync_data()).GetTag());
528 if (result != syncer::BaseNode::INIT_OK) {
529 std::string error_prefix = "Failed to load " + type_str + " node. " +
530 change.location().ToString() + ", ";
531 if (result == syncer::BaseNode::INIT_FAILED_PRECONDITION) {
532 syncer::SyncError error;
533 error.Reset(FROM_HERE, error_prefix + "empty tag", type);
534 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
535 error.message());
536 LOG(ERROR) << "Update: Empty tag.";
537 return error;
538 } else if (result == syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD) {
539 syncer::SyncError error;
540 error.Reset(FROM_HERE, error_prefix + "bad entry", type);
541 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
542 error.message());
543 LOG(ERROR) << "Update: bad entry.";
544 return error;
545 } else if (result == syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL) {
546 syncer::SyncError error;
547 error.Reset(FROM_HERE, error_prefix + "deleted entry", type);
548 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
549 error.message());
550 LOG(ERROR) << "Update: deleted entry.";
551 return error;
552 } else {
553 syncer::Cryptographer* crypto = trans.GetCryptographer();
554 syncer::ModelTypeSet encrypted_types(trans.GetEncryptedTypes());
555 const sync_pb::EntitySpecifics& specifics =
556 sync_node->GetEntry()->GetSpecifics();
557 CHECK(specifics.has_encrypted());
558 const bool can_decrypt = crypto->CanDecrypt(specifics.encrypted());
559 const bool agreement = encrypted_types.Has(type);
560 if (!agreement && !can_decrypt) {
561 syncer::SyncError error;
562 error.Reset(FROM_HERE,
563 "Failed to load encrypted entry, missing key and "
564 "nigori mismatch for " +
565 type_str + ".",
566 type);
567 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
568 error.message());
569 LOG(ERROR) << "Update: encr case 1.";
570 return error;
571 } else if (agreement && can_decrypt) {
572 syncer::SyncError error;
573 error.Reset(FROM_HERE,
574 "Failed to load encrypted entry, we have the key "
575 "and the nigori matches (?!) for " +
576 type_str + ".",
577 type);
578 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
579 error.message());
580 LOG(ERROR) << "Update: encr case 2.";
581 return error;
582 } else if (agreement) {
583 syncer::SyncError error;
584 error.Reset(FROM_HERE,
585 "Failed to load encrypted entry, missing key and "
586 "the nigori matches for " +
587 type_str + ".",
588 type);
589 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
590 error.message());
591 LOG(ERROR) << "Update: encr case 3.";
592 return error;
593 } else {
594 syncer::SyncError error;
595 error.Reset(FROM_HERE,
596 "Failed to load encrypted entry, we have the key"
597 "(?!) and nigori mismatch for " +
598 type_str + ".",
599 type);
600 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
601 error.message());
602 LOG(ERROR) << "Update: encr case 4.";
603 return error;
608 sync_node->SetTitle(change.sync_data().GetTitle());
609 SetNodeSpecifics(change.sync_data().GetSpecifics(), sync_node);
610 attachment_service_->OnSyncDataUpdate(sync_node->GetAttachmentIds(),
611 change.sync_data());
612 if (merge_result_.get()) {
613 merge_result_->set_num_items_modified(merge_result_->num_items_modified() +
616 // TODO(sync): Support updating other parts of the sync node (title,
617 // successor, parent, etc.).
618 return syncer::SyncError();
621 bool GenericChangeProcessor::SyncModelHasUserCreatedNodes(
622 syncer::ModelType type,
623 bool* has_nodes) {
624 DCHECK(CalledOnValidThread());
625 DCHECK(has_nodes);
626 DCHECK_NE(type, syncer::UNSPECIFIED);
627 std::string type_name = syncer::ModelTypeToString(type);
628 std::string err_str = "Server did not create the top-level " + type_name +
629 " node. We might be running against an out-of-date server.";
630 *has_nodes = false;
631 syncer::ReadTransaction trans(FROM_HERE, share_handle());
632 syncer::ReadNode type_root_node(&trans);
633 if (type_root_node.InitByTagLookup(syncer::ModelTypeToRootTag(type)) !=
634 syncer::BaseNode::INIT_OK) {
635 LOG(ERROR) << err_str;
636 return false;
639 // The sync model has user created nodes if the type's root node has any
640 // children.
641 *has_nodes = type_root_node.HasChildren();
642 return true;
645 bool GenericChangeProcessor::CryptoReadyIfNecessary(syncer::ModelType type) {
646 DCHECK(CalledOnValidThread());
647 DCHECK_NE(type, syncer::UNSPECIFIED);
648 // We only access the cryptographer while holding a transaction.
649 syncer::ReadTransaction trans(FROM_HERE, share_handle());
650 const syncer::ModelTypeSet encrypted_types = trans.GetEncryptedTypes();
651 return !encrypted_types.Has(type) ||
652 trans.GetCryptographer()->is_ready();
655 void GenericChangeProcessor::StartImpl() {
656 DCHECK(CalledOnValidThread());
659 syncer::UserShare* GenericChangeProcessor::share_handle() const {
660 DCHECK(CalledOnValidThread());
661 return share_handle_;
664 } // namespace browser_sync