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 "chrome/browser/sync/glue/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 "content/public/browser/browser_thread.h"
11 #include "sync/api/sync_change.h"
12 #include "sync/api/sync_error.h"
13 #include "sync/api/syncable_service.h"
14 #include "sync/internal_api/public/base_node.h"
15 #include "sync/internal_api/public/change_record.h"
16 #include "sync/internal_api/public/read_node.h"
17 #include "sync/internal_api/public/read_transaction.h"
18 #include "sync/internal_api/public/util/unrecoverable_error_handler.h"
19 #include "sync/internal_api/public/write_node.h"
20 #include "sync/internal_api/public/write_transaction.h"
21 #include "sync/syncable/entry.h" // TODO(tim): Bug 123674.
23 using content::BrowserThread
;
25 namespace browser_sync
{
29 void SetNodeSpecifics(const sync_pb::EntitySpecifics
& entity_specifics
,
30 syncer::WriteNode
* write_node
) {
31 if (syncer::GetModelTypeFromSpecifics(entity_specifics
) ==
33 write_node
->SetPasswordSpecifics(
34 entity_specifics
.password().client_only_encrypted_data());
36 write_node
->SetEntitySpecifics(entity_specifics
);
40 syncer::SyncData
BuildRemoteSyncData(
42 const syncer::BaseNode
& read_node
) {
43 // Use the specifics of non-password datatypes directly (encryption has
44 // already been handled).
45 if (read_node
.GetModelType() != syncer::PASSWORDS
) {
46 return syncer::SyncData::CreateRemoteData(sync_id
,
47 read_node
.GetEntitySpecifics(),
48 read_node
.GetModificationTime());
51 // Passwords must be accessed differently, to account for their encryption,
52 // and stored into a temporary EntitySpecifics.
53 sync_pb::EntitySpecifics password_holder
;
54 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
55 CopyFrom(read_node
.GetPasswordSpecifics());
56 return syncer::SyncData::CreateRemoteData(sync_id
,
58 read_node
.GetModificationTime());
63 GenericChangeProcessor::GenericChangeProcessor(
64 DataTypeErrorHandler
* error_handler
,
65 const base::WeakPtr
<syncer::SyncableService
>& local_service
,
66 const base::WeakPtr
<syncer::SyncMergeResult
>& merge_result
,
67 syncer::UserShare
* user_share
)
68 : ChangeProcessor(error_handler
),
69 local_service_(local_service
),
70 merge_result_(merge_result
),
71 share_handle_(user_share
) {
72 DCHECK(CalledOnValidThread());
75 GenericChangeProcessor::~GenericChangeProcessor() {
76 DCHECK(CalledOnValidThread());
79 void GenericChangeProcessor::ApplyChangesFromSyncModel(
80 const syncer::BaseTransaction
* trans
,
82 const syncer::ImmutableChangeRecordList
& changes
) {
83 DCHECK(CalledOnValidThread());
84 DCHECK(syncer_changes_
.empty());
85 for (syncer::ChangeRecordList::const_iterator it
=
86 changes
.Get().begin(); it
!= changes
.Get().end(); ++it
) {
87 if (it
->action
== syncer::ChangeRecord::ACTION_DELETE
) {
88 syncer_changes_
.push_back(
91 syncer::SyncChange::ACTION_DELETE
,
92 syncer::SyncData::CreateRemoteData(
93 it
->id
, it
->specifics
, base::Time())));
95 syncer::SyncChange::SyncChangeType action
=
96 (it
->action
== syncer::ChangeRecord::ACTION_ADD
) ?
97 syncer::SyncChange::ACTION_ADD
: syncer::SyncChange::ACTION_UPDATE
;
98 // Need to load specifics from node.
99 syncer::ReadNode
read_node(trans
);
100 if (read_node
.InitByIdLookup(it
->id
) != syncer::BaseNode::INIT_OK
) {
101 error_handler()->OnSingleDatatypeUnrecoverableError(
103 "Failed to look up data for received change with id " +
104 base::Int64ToString(it
->id
));
107 syncer_changes_
.push_back(
111 BuildRemoteSyncData(it
->id
, read_node
)));
116 void GenericChangeProcessor::CommitChangesFromSyncModel() {
117 DCHECK(CalledOnValidThread());
118 if (syncer_changes_
.empty())
120 if (!local_service_
.get()) {
121 syncer::ModelType type
= syncer_changes_
[0].sync_data().GetDataType();
122 syncer::SyncError
error(FROM_HERE
,
123 syncer::SyncError::DATATYPE_ERROR
,
124 "Local service destroyed.",
126 error_handler()->OnSingleDatatypeUnrecoverableError(error
.location(),
130 syncer::SyncError error
= local_service_
->ProcessSyncChanges(FROM_HERE
,
132 syncer_changes_
.clear();
134 error_handler()->OnSingleDatatypeUnrecoverableError(
135 error
.location(), error
.message());
139 syncer::SyncDataList
GenericChangeProcessor::GetAllSyncData(
140 syncer::ModelType type
) const {
141 // This is slow / memory intensive. Should be used sparingly by datatypes.
142 syncer::SyncDataList data
;
143 GetAllSyncDataReturnError(type
, &data
);
147 syncer::SyncError
GenericChangeProcessor::GetAllSyncDataReturnError(
148 syncer::ModelType type
,
149 syncer::SyncDataList
* current_sync_data
) const {
150 DCHECK(CalledOnValidThread());
151 std::string type_name
= syncer::ModelTypeToString(type
);
152 syncer::ReadTransaction
trans(FROM_HERE
, share_handle());
153 syncer::ReadNode
root(&trans
);
154 if (root
.InitByTagLookup(syncer::ModelTypeToRootTag(type
)) !=
155 syncer::BaseNode::INIT_OK
) {
156 syncer::SyncError
error(FROM_HERE
,
157 syncer::SyncError::DATATYPE_ERROR
,
158 "Server did not create the top-level " + type_name
+
159 " node. We might be running against an out-of-"
165 // TODO(akalin): We'll have to do a tree traversal for bookmarks.
166 DCHECK_NE(type
, syncer::BOOKMARKS
);
168 std::vector
<int64
> child_ids
;
169 root
.GetChildIds(&child_ids
);
171 for (std::vector
<int64
>::iterator it
= child_ids
.begin();
172 it
!= child_ids
.end(); ++it
) {
173 syncer::ReadNode
sync_child_node(&trans
);
174 if (sync_child_node
.InitByIdLookup(*it
) !=
175 syncer::BaseNode::INIT_OK
) {
176 syncer::SyncError
error(FROM_HERE
,
177 syncer::SyncError::DATATYPE_ERROR
,
178 "Failed to fetch child node for type " +
183 current_sync_data
->push_back(BuildRemoteSyncData(sync_child_node
.GetId(),
186 return syncer::SyncError();
189 int GenericChangeProcessor::GetSyncCountForType(syncer::ModelType type
) {
190 syncer::ReadTransaction
trans(FROM_HERE
, share_handle());
191 syncer::ReadNode
root(&trans
);
192 if (root
.InitByTagLookup(syncer::ModelTypeToRootTag(type
)) !=
193 syncer::BaseNode::INIT_OK
)
196 // Subtract one to account for type's root node.
197 return root
.GetTotalNodeCount() - 1;
202 // TODO(isherman): Investigating http://crbug.com/121592
203 // WARNING: this code is sensitive to compiler optimizations. Be careful
204 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
205 // the compiler attempts to merge it with other calls, losing useful information
206 // in breakpad uploads.
207 syncer::SyncError
LogLookupFailure(
208 syncer::BaseNode::InitByLookupResult lookup_result
,
209 const tracked_objects::Location
& from_here
,
210 const std::string
& error_prefix
,
211 syncer::ModelType type
,
212 DataTypeErrorHandler
* error_handler
) {
213 switch (lookup_result
) {
214 case syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD
: {
215 syncer::SyncError error
;
216 error
.Reset(from_here
,
218 "could not find entry matching the lookup criteria.",
220 error_handler
->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
222 LOG(ERROR
) << "Delete: Bad entry.";
225 case syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL
: {
226 syncer::SyncError error
;
227 error
.Reset(from_here
, error_prefix
+ "entry is already deleted.", type
);
228 error_handler
->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
230 LOG(ERROR
) << "Delete: Deleted entry.";
233 case syncer::BaseNode::INIT_FAILED_DECRYPT_IF_NECESSARY
: {
234 syncer::SyncError error
;
235 error
.Reset(from_here
, error_prefix
+ "unable to decrypt", type
);
236 error_handler
->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
238 LOG(ERROR
) << "Delete: Undecryptable entry.";
241 case syncer::BaseNode::INIT_FAILED_PRECONDITION
: {
242 syncer::SyncError error
;
243 error
.Reset(from_here
,
244 error_prefix
+ "a precondition was not met for calling init.",
246 error_handler
->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
248 LOG(ERROR
) << "Delete: Failed precondition.";
252 syncer::SyncError error
;
253 // Should have listed all the possible error cases above.
254 error
.Reset(from_here
, error_prefix
+ "unknown error", type
);
255 error_handler
->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
257 LOG(ERROR
) << "Delete: Unknown error.";
263 syncer::SyncError
AttemptDelete(
264 const syncer::SyncChange
& change
,
265 syncer::ModelType type
,
266 const std::string
& type_str
,
267 syncer::WriteNode
* node
,
268 DataTypeErrorHandler
* error_handler
) {
269 DCHECK_EQ(change
.change_type(), syncer::SyncChange::ACTION_DELETE
);
270 if (change
.sync_data().IsLocal()) {
271 const std::string
& tag
= change
.sync_data().GetTag();
273 syncer::SyncError
error(
275 syncer::SyncError::DATATYPE_ERROR
,
276 "Failed to delete " + type_str
+ " node. Local data, empty tag. " +
277 change
.location().ToString(),
279 error_handler
->OnSingleDatatypeUnrecoverableError(error
.location(),
285 syncer::BaseNode::InitByLookupResult result
=
286 node
->InitByClientTagLookup(change
.sync_data().GetDataType(), tag
);
287 if (result
!= syncer::BaseNode::INIT_OK
) {
288 return LogLookupFailure(
290 "Failed to delete " + type_str
+ " node. Local data. " +
291 change
.location().ToString(),
292 type
, error_handler
);
295 syncer::BaseNode::InitByLookupResult result
=
296 node
->InitByIdLookup(change
.sync_data().GetRemoteId());
297 if (result
!= syncer::BaseNode::INIT_OK
) {
298 return LogLookupFailure(
300 "Failed to delete " + type_str
+ " node. Non-local data. " +
301 change
.location().ToString(),
302 type
, error_handler
);
305 if (IsActOnceDataType(type
))
309 return syncer::SyncError();
314 // WARNING: this code is sensitive to compiler optimizations. Be careful
315 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
316 // the compiler attempts to merge it with other calls, losing useful information
317 // in breakpad uploads.
318 syncer::SyncError
GenericChangeProcessor::ProcessSyncChanges(
319 const tracked_objects::Location
& from_here
,
320 const syncer::SyncChangeList
& list_of_changes
) {
321 DCHECK(CalledOnValidThread());
322 syncer::WriteTransaction
trans(from_here
, share_handle());
324 for (syncer::SyncChangeList::const_iterator iter
= list_of_changes
.begin();
325 iter
!= list_of_changes
.end();
327 const syncer::SyncChange
& change
= *iter
;
328 DCHECK_NE(change
.sync_data().GetDataType(), syncer::UNSPECIFIED
);
329 syncer::ModelType type
= change
.sync_data().GetDataType();
330 std::string type_str
= syncer::ModelTypeToString(type
);
331 syncer::WriteNode
sync_node(&trans
);
332 if (change
.change_type() == syncer::SyncChange::ACTION_DELETE
) {
333 syncer::SyncError error
=
334 AttemptDelete(change
, type
, type_str
, &sync_node
,
340 if (merge_result_
.get()) {
341 merge_result_
->set_num_items_deleted(
342 merge_result_
->num_items_deleted() + 1);
344 } else if (change
.change_type() == syncer::SyncChange::ACTION_ADD
) {
345 // TODO(sync): Handle other types of creation (custom parents, folders,
347 syncer::ReadNode
root_node(&trans
);
348 if (root_node
.InitByTagLookup(
349 syncer::ModelTypeToRootTag(change
.sync_data().GetDataType())) !=
350 syncer::BaseNode::INIT_OK
) {
351 syncer::SyncError
error(FROM_HERE
,
352 syncer::SyncError::DATATYPE_ERROR
,
353 "Failed to look up root node for type " +
356 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
359 LOG(ERROR
) << "Create: no root node.";
362 syncer::WriteNode::InitUniqueByCreationResult result
=
363 sync_node
.InitUniqueByCreation(change
.sync_data().GetDataType(),
365 change
.sync_data().GetTag());
366 if (result
!= syncer::WriteNode::INIT_SUCCESS
) {
367 std::string error_prefix
= "Failed to create " + type_str
+ " node: " +
368 change
.location().ToString() + ", ";
370 case syncer::WriteNode::INIT_FAILED_EMPTY_TAG
: {
371 syncer::SyncError error
;
372 error
.Reset(FROM_HERE
, error_prefix
+ "empty tag", type
);
373 error_handler()->OnSingleDatatypeUnrecoverableError(
374 FROM_HERE
, error
.message());
375 LOG(ERROR
) << "Create: Empty tag.";
378 case syncer::WriteNode::INIT_FAILED_ENTRY_ALREADY_EXISTS
: {
379 syncer::SyncError error
;
380 error
.Reset(FROM_HERE
, error_prefix
+ "entry already exists", type
);
381 error_handler()->OnSingleDatatypeUnrecoverableError(
382 FROM_HERE
, error
.message());
383 LOG(ERROR
) << "Create: Entry exists.";
386 case syncer::WriteNode::INIT_FAILED_COULD_NOT_CREATE_ENTRY
: {
387 syncer::SyncError error
;
388 error
.Reset(FROM_HERE
, error_prefix
+ "failed to create entry",
390 error_handler()->OnSingleDatatypeUnrecoverableError(
391 FROM_HERE
, error
.message());
392 LOG(ERROR
) << "Create: Could not create entry.";
395 case syncer::WriteNode::INIT_FAILED_SET_PREDECESSOR
: {
396 syncer::SyncError error
;
397 error
.Reset(FROM_HERE
, error_prefix
+ "failed to set predecessor",
399 error_handler()->OnSingleDatatypeUnrecoverableError(
400 FROM_HERE
, error
.message());
401 LOG(ERROR
) << "Create: Bad predecessor.";
405 syncer::SyncError error
;
406 error
.Reset(FROM_HERE
, error_prefix
+ "unknown error", type
);
407 error_handler()->OnSingleDatatypeUnrecoverableError(
408 FROM_HERE
, error
.message());
409 LOG(ERROR
) << "Create: Unknown error.";
414 sync_node
.SetTitle(base::UTF8ToWide(change
.sync_data().GetTitle()));
415 SetNodeSpecifics(change
.sync_data().GetSpecifics(), &sync_node
);
416 if (merge_result_
.get()) {
417 merge_result_
->set_num_items_added(merge_result_
->num_items_added() +
420 } else if (change
.change_type() == syncer::SyncChange::ACTION_UPDATE
) {
421 // TODO(zea): consider having this logic for all possible changes?
422 syncer::BaseNode::InitByLookupResult result
=
423 sync_node
.InitByClientTagLookup(change
.sync_data().GetDataType(),
424 change
.sync_data().GetTag());
425 if (result
!= syncer::BaseNode::INIT_OK
) {
426 std::string error_prefix
= "Failed to load " + type_str
+ " node. " +
427 change
.location().ToString() + ", ";
428 if (result
== syncer::BaseNode::INIT_FAILED_PRECONDITION
) {
429 syncer::SyncError error
;
430 error
.Reset(FROM_HERE
,
431 error_prefix
+ "empty tag",
433 error_handler()->OnSingleDatatypeUnrecoverableError(
434 FROM_HERE
, error
.message());
435 LOG(ERROR
) << "Update: Empty tag.";
437 } else if (result
== syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD
) {
438 syncer::SyncError error
;
439 error
.Reset(FROM_HERE
,
440 error_prefix
+ "bad entry",
442 error_handler()->OnSingleDatatypeUnrecoverableError(
443 FROM_HERE
, error
.message());
444 LOG(ERROR
) << "Update: bad entry.";
446 } else if (result
== syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL
) {
447 syncer::SyncError error
;
448 error
.Reset(FROM_HERE
,
449 error_prefix
+ "deleted entry",
451 error_handler()->OnSingleDatatypeUnrecoverableError(
452 FROM_HERE
, error
.message());
453 LOG(ERROR
) << "Update: deleted entry.";
456 syncer::Cryptographer
* crypto
= trans
.GetCryptographer();
457 syncer::ModelTypeSet
encrypted_types(trans
.GetEncryptedTypes());
458 const sync_pb::EntitySpecifics
& specifics
=
459 sync_node
.GetEntry()->GetSpecifics();
460 CHECK(specifics
.has_encrypted());
461 const bool can_decrypt
= crypto
->CanDecrypt(specifics
.encrypted());
462 const bool agreement
= encrypted_types
.Has(type
);
463 if (!agreement
&& !can_decrypt
) {
464 syncer::SyncError error
;
465 error
.Reset(FROM_HERE
,
466 "Failed to load encrypted entry, missing key and "
467 "nigori mismatch for " + type_str
+ ".",
469 error_handler()->OnSingleDatatypeUnrecoverableError(
470 FROM_HERE
, error
.message());
471 LOG(ERROR
) << "Update: encr case 1.";
473 } else if (agreement
&& can_decrypt
) {
474 syncer::SyncError error
;
475 error
.Reset(FROM_HERE
,
476 "Failed to load encrypted entry, we have the key "
477 "and the nigori matches (?!) for " + type_str
+ ".",
479 error_handler()->OnSingleDatatypeUnrecoverableError(
480 FROM_HERE
, error
.message());
481 LOG(ERROR
) << "Update: encr case 2.";
483 } else if (agreement
) {
484 syncer::SyncError error
;
485 error
.Reset(FROM_HERE
,
486 "Failed to load encrypted entry, missing key and "
487 "the nigori matches for " + type_str
+ ".",
489 error_handler()->OnSingleDatatypeUnrecoverableError(
490 FROM_HERE
, error
.message());
491 LOG(ERROR
) << "Update: encr case 3.";
494 syncer::SyncError error
;
495 error
.Reset(FROM_HERE
,
496 "Failed to load encrypted entry, we have the key"
497 "(?!) and nigori mismatch for " + type_str
+ ".",
499 error_handler()->OnSingleDatatypeUnrecoverableError(
500 FROM_HERE
, error
.message());
501 LOG(ERROR
) << "Update: encr case 4.";
507 sync_node
.SetTitle(base::UTF8ToWide(change
.sync_data().GetTitle()));
508 SetNodeSpecifics(change
.sync_data().GetSpecifics(), &sync_node
);
509 if (merge_result_
.get()) {
510 merge_result_
->set_num_items_modified(
511 merge_result_
->num_items_modified() + 1);
513 // TODO(sync): Support updating other parts of the sync node (title,
514 // successor, parent, etc.).
516 syncer::SyncError
error(
518 syncer::SyncError::DATATYPE_ERROR
,
519 "Received unset SyncChange in the change processor, " +
520 change
.location().ToString(),
522 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
525 LOG(ERROR
) << "Unset sync change.";
529 return syncer::SyncError();
532 bool GenericChangeProcessor::SyncModelHasUserCreatedNodes(
533 syncer::ModelType type
,
535 DCHECK(CalledOnValidThread());
537 DCHECK_NE(type
, syncer::UNSPECIFIED
);
538 std::string type_name
= syncer::ModelTypeToString(type
);
539 std::string err_str
= "Server did not create the top-level " + type_name
+
540 " node. We might be running against an out-of-date server.";
542 syncer::ReadTransaction
trans(FROM_HERE
, share_handle());
543 syncer::ReadNode
type_root_node(&trans
);
544 if (type_root_node
.InitByTagLookup(syncer::ModelTypeToRootTag(type
)) !=
545 syncer::BaseNode::INIT_OK
) {
546 LOG(ERROR
) << err_str
;
550 // The sync model has user created nodes if the type's root node has any
552 *has_nodes
= type_root_node
.HasChildren();
556 bool GenericChangeProcessor::CryptoReadyIfNecessary(syncer::ModelType type
) {
557 DCHECK(CalledOnValidThread());
558 DCHECK_NE(type
, syncer::UNSPECIFIED
);
559 // We only access the cryptographer while holding a transaction.
560 syncer::ReadTransaction
trans(FROM_HERE
, share_handle());
561 const syncer::ModelTypeSet encrypted_types
= trans
.GetEncryptedTypes();
562 return !encrypted_types
.Has(type
) ||
563 trans
.GetCryptographer()->is_ready();
566 void GenericChangeProcessor::StartImpl(Profile
* profile
) {
567 DCHECK(CalledOnValidThread());
570 syncer::UserShare
* GenericChangeProcessor::share_handle() const {
571 DCHECK(CalledOnValidThread());
572 return share_handle_
;
575 } // namespace browser_sync