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/string_number_conversions.h"
9 #include "base/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
{
27 GenericChangeProcessor::GenericChangeProcessor(
28 DataTypeErrorHandler
* error_handler
,
29 const base::WeakPtr
<syncer::SyncableService
>& local_service
,
30 syncer::UserShare
* user_share
)
31 : ChangeProcessor(error_handler
),
32 local_service_(local_service
),
33 share_handle_(user_share
) {
34 DCHECK(CalledOnValidThread());
37 GenericChangeProcessor::~GenericChangeProcessor() {
38 DCHECK(CalledOnValidThread());
41 void GenericChangeProcessor::ApplyChangesFromSyncModel(
42 const syncer::BaseTransaction
* trans
,
44 const syncer::ImmutableChangeRecordList
& changes
) {
45 DCHECK(CalledOnValidThread());
46 DCHECK(syncer_changes_
.empty());
47 for (syncer::ChangeRecordList::const_iterator it
=
48 changes
.Get().begin(); it
!= changes
.Get().end(); ++it
) {
49 if (it
->action
== syncer::ChangeRecord::ACTION_DELETE
) {
50 syncer_changes_
.push_back(
53 syncer::SyncChange::ACTION_DELETE
,
54 syncer::SyncData::CreateRemoteData(it
->id
, it
->specifics
)));
56 syncer::SyncChange::SyncChangeType action
=
57 (it
->action
== syncer::ChangeRecord::ACTION_ADD
) ?
58 syncer::SyncChange::ACTION_ADD
: syncer::SyncChange::ACTION_UPDATE
;
59 // Need to load specifics from node.
60 syncer::ReadNode
read_node(trans
);
61 if (read_node
.InitByIdLookup(it
->id
) != syncer::BaseNode::INIT_OK
) {
62 error_handler()->OnSingleDatatypeUnrecoverableError(
64 "Failed to look up data for received change with id " +
65 base::Int64ToString(it
->id
));
68 syncer_changes_
.push_back(
72 syncer::SyncData::CreateRemoteData(
73 it
->id
, read_node
.GetEntitySpecifics())));
78 void GenericChangeProcessor::CommitChangesFromSyncModel() {
79 DCHECK(CalledOnValidThread());
80 if (syncer_changes_
.empty())
82 if (!local_service_
) {
83 syncer::ModelType type
= syncer_changes_
[0].sync_data().GetDataType();
84 syncer::SyncError
error(FROM_HERE
, "Local service destroyed.", type
);
85 error_handler()->OnSingleDatatypeUnrecoverableError(error
.location(),
89 syncer::SyncError error
= local_service_
->ProcessSyncChanges(FROM_HERE
,
91 syncer_changes_
.clear();
93 error_handler()->OnSingleDatatypeUnrecoverableError(
94 error
.location(), error
.message());
98 syncer::SyncError
GenericChangeProcessor::GetSyncDataForType(
99 syncer::ModelType type
,
100 syncer::SyncDataList
* current_sync_data
) {
101 DCHECK(CalledOnValidThread());
102 std::string type_name
= syncer::ModelTypeToString(type
);
103 syncer::ReadTransaction
trans(FROM_HERE
, share_handle());
104 syncer::ReadNode
root(&trans
);
105 if (root
.InitByTagLookup(syncer::ModelTypeToRootTag(type
)) !=
106 syncer::BaseNode::INIT_OK
) {
107 syncer::SyncError
error(FROM_HERE
,
108 "Server did not create the top-level " + type_name
+
109 " node. We might be running against an out-of-date server.",
114 // TODO(akalin): We'll have to do a tree traversal for bookmarks.
115 DCHECK_NE(type
, syncer::BOOKMARKS
);
117 int64 sync_child_id
= root
.GetFirstChildId();
118 while (sync_child_id
!= syncer::kInvalidId
) {
119 syncer::ReadNode
sync_child_node(&trans
);
120 if (sync_child_node
.InitByIdLookup(sync_child_id
) !=
121 syncer::BaseNode::INIT_OK
) {
122 syncer::SyncError
error(FROM_HERE
,
123 "Failed to fetch child node for type " + type_name
+ ".",
127 current_sync_data
->push_back(syncer::SyncData::CreateRemoteData(
128 sync_child_node
.GetId(), sync_child_node
.GetEntitySpecifics()));
129 sync_child_id
= sync_child_node
.GetSuccessorId();
131 return syncer::SyncError();
136 // TODO(isherman): Investigating http://crbug.com/121592
137 // WARNING: this code is sensitive to compiler optimizations. Be careful
138 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
139 // the compiler attempts to merge it with other calls, losing useful information
140 // in breakpad uploads.
141 syncer::SyncError
LogLookupFailure(
142 syncer::BaseNode::InitByLookupResult lookup_result
,
143 const tracked_objects::Location
& from_here
,
144 const std::string
& error_prefix
,
145 syncer::ModelType type
,
146 DataTypeErrorHandler
* error_handler
) {
147 switch (lookup_result
) {
148 case syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD
: {
149 syncer::SyncError error
;
150 error
.Reset(from_here
,
152 "could not find entry matching the lookup criteria.",
154 error_handler
->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
156 LOG(ERROR
) << "Delete: Bad entry.";
159 case syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL
: {
160 syncer::SyncError error
;
161 error
.Reset(from_here
, error_prefix
+ "entry is already deleted.", type
);
162 error_handler
->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
164 LOG(ERROR
) << "Delete: Deleted entry.";
167 case syncer::BaseNode::INIT_FAILED_DECRYPT_IF_NECESSARY
: {
168 syncer::SyncError error
;
169 error
.Reset(from_here
, error_prefix
+ "unable to decrypt", type
);
170 error_handler
->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
172 LOG(ERROR
) << "Delete: Undecryptable entry.";
175 case syncer::BaseNode::INIT_FAILED_PRECONDITION
: {
176 syncer::SyncError error
;
177 error
.Reset(from_here
,
178 error_prefix
+ "a precondition was not met for calling init.",
180 error_handler
->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
182 LOG(ERROR
) << "Delete: Failed precondition.";
186 syncer::SyncError error
;
187 // Should have listed all the possible error cases above.
188 error
.Reset(from_here
, error_prefix
+ "unknown error", type
);
189 error_handler
->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
191 LOG(ERROR
) << "Delete: Unknown error.";
197 syncer::SyncError
AttemptDelete(
198 const syncer::SyncChange
& change
,
199 syncer::ModelType type
,
200 const std::string
& type_str
,
201 syncer::WriteNode
* node
,
202 DataTypeErrorHandler
* error_handler
) {
203 DCHECK_EQ(change
.change_type(), syncer::SyncChange::ACTION_DELETE
);
204 if (change
.sync_data().IsLocal()) {
205 const std::string
& tag
= change
.sync_data().GetTag();
207 syncer::SyncError
error(
209 "Failed to delete " + type_str
+ " node. Local data, empty tag. " +
210 change
.location().ToString(),
212 error_handler
->OnSingleDatatypeUnrecoverableError(error
.location(),
218 syncer::BaseNode::InitByLookupResult result
=
219 node
->InitByClientTagLookup(change
.sync_data().GetDataType(), tag
);
220 if (result
!= syncer::BaseNode::INIT_OK
) {
221 return LogLookupFailure(
223 "Failed to delete " + type_str
+ " node. Local data. " +
224 change
.location().ToString(),
225 type
, error_handler
);
228 syncer::BaseNode::InitByLookupResult result
=
229 node
->InitByIdLookup(change
.sync_data().GetRemoteId());
230 if (result
!= syncer::BaseNode::INIT_OK
) {
231 return LogLookupFailure(
233 "Failed to delete " + type_str
+ " node. Non-local data. " +
234 change
.location().ToString(),
235 type
, error_handler
);
239 return syncer::SyncError();
244 // WARNING: this code is sensitive to compiler optimizations. Be careful
245 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
246 // the compiler attempts to merge it with other calls, losing useful information
247 // in breakpad uploads.
248 syncer::SyncError
GenericChangeProcessor::ProcessSyncChanges(
249 const tracked_objects::Location
& from_here
,
250 const syncer::SyncChangeList
& list_of_changes
) {
251 DCHECK(CalledOnValidThread());
252 syncer::WriteTransaction
trans(from_here
, share_handle());
254 for (syncer::SyncChangeList::const_iterator iter
= list_of_changes
.begin();
255 iter
!= list_of_changes
.end();
257 const syncer::SyncChange
& change
= *iter
;
258 DCHECK_NE(change
.sync_data().GetDataType(), syncer::UNSPECIFIED
);
259 syncer::ModelType type
= change
.sync_data().GetDataType();
260 std::string type_str
= syncer::ModelTypeToString(type
);
261 syncer::WriteNode
sync_node(&trans
);
262 if (change
.change_type() == syncer::SyncChange::ACTION_DELETE
) {
263 syncer::SyncError error
=
264 AttemptDelete(change
, type
, type_str
, &sync_node
,
270 } else if (change
.change_type() == syncer::SyncChange::ACTION_ADD
) {
271 // TODO(sync): Handle other types of creation (custom parents, folders,
273 syncer::ReadNode
root_node(&trans
);
274 if (root_node
.InitByTagLookup(
275 syncer::ModelTypeToRootTag(change
.sync_data().GetDataType())) !=
276 syncer::BaseNode::INIT_OK
) {
277 syncer::SyncError
error(FROM_HERE
,
278 "Failed to look up root node for type " + type_str
,
280 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
283 LOG(ERROR
) << "Create: no root node.";
286 syncer::WriteNode::InitUniqueByCreationResult result
=
287 sync_node
.InitUniqueByCreation(change
.sync_data().GetDataType(),
289 change
.sync_data().GetTag());
290 if (result
!= syncer::WriteNode::INIT_SUCCESS
) {
291 std::string error_prefix
= "Failed to create " + type_str
+ " node: " +
292 change
.location().ToString() + ", ";
294 case syncer::WriteNode::INIT_FAILED_EMPTY_TAG
: {
295 syncer::SyncError error
;
296 error
.Reset(FROM_HERE
, error_prefix
+ "empty tag", type
);
297 error_handler()->OnSingleDatatypeUnrecoverableError(
298 FROM_HERE
, error
.message());
299 LOG(ERROR
) << "Create: Empty tag.";
302 case syncer::WriteNode::INIT_FAILED_ENTRY_ALREADY_EXISTS
: {
303 syncer::SyncError error
;
304 error
.Reset(FROM_HERE
, error_prefix
+ "entry already exists", type
);
305 error_handler()->OnSingleDatatypeUnrecoverableError(
306 FROM_HERE
, error
.message());
307 LOG(ERROR
) << "Create: Entry exists.";
310 case syncer::WriteNode::INIT_FAILED_COULD_NOT_CREATE_ENTRY
: {
311 syncer::SyncError error
;
312 error
.Reset(FROM_HERE
, error_prefix
+ "failed to create entry",
314 error_handler()->OnSingleDatatypeUnrecoverableError(
315 FROM_HERE
, error
.message());
316 LOG(ERROR
) << "Create: Could not create entry.";
319 case syncer::WriteNode::INIT_FAILED_SET_PREDECESSOR
: {
320 syncer::SyncError error
;
321 error
.Reset(FROM_HERE
, error_prefix
+ "failed to set predecessor",
323 error_handler()->OnSingleDatatypeUnrecoverableError(
324 FROM_HERE
, error
.message());
325 LOG(ERROR
) << "Create: Bad predecessor.";
329 syncer::SyncError error
;
330 error
.Reset(FROM_HERE
, error_prefix
+ "unknown error", type
);
331 error_handler()->OnSingleDatatypeUnrecoverableError(
332 FROM_HERE
, error
.message());
333 LOG(ERROR
) << "Create: Unknown error.";
338 sync_node
.SetTitle(UTF8ToWide(change
.sync_data().GetTitle()));
339 sync_node
.SetEntitySpecifics(change
.sync_data().GetSpecifics());
340 } else if (change
.change_type() == syncer::SyncChange::ACTION_UPDATE
) {
341 // TODO(zea): consider having this logic for all possible changes?
342 syncer::BaseNode::InitByLookupResult result
=
343 sync_node
.InitByClientTagLookup(change
.sync_data().GetDataType(),
344 change
.sync_data().GetTag());
345 if (result
!= syncer::BaseNode::INIT_OK
) {
346 std::string error_prefix
= "Failed to load " + type_str
+ " node. " +
347 change
.location().ToString() + ", ";
348 if (result
== syncer::BaseNode::INIT_FAILED_PRECONDITION
) {
349 syncer::SyncError error
;
350 error
.Reset(FROM_HERE
,
351 error_prefix
+ "empty tag",
353 error_handler()->OnSingleDatatypeUnrecoverableError(
354 FROM_HERE
, error
.message());
355 LOG(ERROR
) << "Update: Empty tag.";
357 } else if (result
== syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD
) {
358 syncer::SyncError error
;
359 error
.Reset(FROM_HERE
,
360 error_prefix
+ "bad entry",
362 error_handler()->OnSingleDatatypeUnrecoverableError(
363 FROM_HERE
, error
.message());
364 LOG(ERROR
) << "Update: bad entry.";
366 } else if (result
== syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL
) {
367 syncer::SyncError error
;
368 error
.Reset(FROM_HERE
,
369 error_prefix
+ "deleted entry",
371 error_handler()->OnSingleDatatypeUnrecoverableError(
372 FROM_HERE
, error
.message());
373 LOG(ERROR
) << "Update: deleted entry.";
376 syncer::Cryptographer
* crypto
= trans
.GetCryptographer();
377 syncer::ModelTypeSet
encrypted_types(trans
.GetEncryptedTypes());
378 const sync_pb::EntitySpecifics
& specifics
=
379 sync_node
.GetEntry()->Get(syncer::syncable::SPECIFICS
);
380 CHECK(specifics
.has_encrypted());
381 const bool can_decrypt
= crypto
->CanDecrypt(specifics
.encrypted());
382 const bool agreement
= encrypted_types
.Has(type
);
383 if (!agreement
&& !can_decrypt
) {
384 syncer::SyncError error
;
385 error
.Reset(FROM_HERE
,
386 "Failed to load encrypted entry, missing key and "
387 "nigori mismatch for " + type_str
+ ".",
389 error_handler()->OnSingleDatatypeUnrecoverableError(
390 FROM_HERE
, error
.message());
391 LOG(ERROR
) << "Update: encr case 1.";
393 } else if (agreement
&& can_decrypt
) {
394 syncer::SyncError error
;
395 error
.Reset(FROM_HERE
,
396 "Failed to load encrypted entry, we have the key "
397 "and the nigori matches (?!) for " + type_str
+ ".",
399 error_handler()->OnSingleDatatypeUnrecoverableError(
400 FROM_HERE
, error
.message());
401 LOG(ERROR
) << "Update: encr case 2.";
403 } else if (agreement
) {
404 syncer::SyncError error
;
405 error
.Reset(FROM_HERE
,
406 "Failed to load encrypted entry, missing key and "
407 "the nigori matches for " + type_str
+ ".",
409 error_handler()->OnSingleDatatypeUnrecoverableError(
410 FROM_HERE
, error
.message());
411 LOG(ERROR
) << "Update: encr case 3.";
414 syncer::SyncError error
;
415 error
.Reset(FROM_HERE
,
416 "Failed to load encrypted entry, we have the key"
417 "(?!) and nigori mismatch for " + type_str
+ ".",
419 error_handler()->OnSingleDatatypeUnrecoverableError(
420 FROM_HERE
, error
.message());
421 LOG(ERROR
) << "Update: encr case 4.";
426 sync_node
.SetTitle(UTF8ToWide(change
.sync_data().GetTitle()));
427 sync_node
.SetEntitySpecifics(change
.sync_data().GetSpecifics());
428 // TODO(sync): Support updating other parts of the sync node (title,
429 // successor, parent, etc.).
432 syncer::SyncError
error(
434 "Received unset SyncChange in the change processor, " +
435 change
.location().ToString(),
437 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
440 LOG(ERROR
) << "Unset sync change.";
444 return syncer::SyncError();
447 bool GenericChangeProcessor::SyncModelHasUserCreatedNodes(
448 syncer::ModelType type
,
450 DCHECK(CalledOnValidThread());
452 DCHECK_NE(type
, syncer::UNSPECIFIED
);
453 std::string type_name
= syncer::ModelTypeToString(type
);
454 std::string err_str
= "Server did not create the top-level " + type_name
+
455 " node. We might be running against an out-of-date server.";
457 syncer::ReadTransaction
trans(FROM_HERE
, share_handle());
458 syncer::ReadNode
type_root_node(&trans
);
459 if (type_root_node
.InitByTagLookup(syncer::ModelTypeToRootTag(type
)) !=
460 syncer::BaseNode::INIT_OK
) {
461 LOG(ERROR
) << err_str
;
465 // The sync model has user created nodes if the type's root node has any
467 *has_nodes
= type_root_node
.HasChildren();
471 bool GenericChangeProcessor::CryptoReadyIfNecessary(syncer::ModelType type
) {
472 DCHECK(CalledOnValidThread());
473 DCHECK_NE(type
, syncer::UNSPECIFIED
);
474 // We only access the cryptographer while holding a transaction.
475 syncer::ReadTransaction
trans(FROM_HERE
, share_handle());
476 const syncer::ModelTypeSet encrypted_types
= trans
.GetEncryptedTypes();
477 return !encrypted_types
.Has(type
) ||
478 trans
.GetCryptographer()->is_ready();
481 void GenericChangeProcessor::StartImpl(Profile
* profile
) {
482 DCHECK(CalledOnValidThread());
485 syncer::UserShare
* GenericChangeProcessor::share_handle() const {
486 DCHECK(CalledOnValidThread());
487 return share_handle_
;
490 } // namespace browser_sync