Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync / glue / generic_change_processor.cc
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.
4
5 #include "chrome/browser/sync/glue/generic_change_processor.h"
6
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.
22
23 using content::BrowserThread;
24
25 namespace browser_sync {
26
27 namespace {
28
29 void SetNodeSpecifics(const sync_pb::EntitySpecifics& entity_specifics,
30                       syncer::WriteNode* write_node) {
31   if (syncer::GetModelTypeFromSpecifics(entity_specifics) ==
32           syncer::PASSWORDS) {
33     write_node->SetPasswordSpecifics(
34         entity_specifics.password().client_only_encrypted_data());
35   } else {
36     write_node->SetEntitySpecifics(entity_specifics);
37   }
38 }
39
40 syncer::SyncData BuildRemoteSyncData(
41     int64 sync_id,
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());
49   }
50
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,
57                                             password_holder,
58                                             read_node.GetModificationTime());
59 }
60
61 }  // namespace
62
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());
73 }
74
75 GenericChangeProcessor::~GenericChangeProcessor() {
76   DCHECK(CalledOnValidThread());
77 }
78
79 void GenericChangeProcessor::ApplyChangesFromSyncModel(
80     const syncer::BaseTransaction* trans,
81     int64 model_version,
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       scoped_ptr<sync_pb::EntitySpecifics> specifics;
89       if (it->specifics.has_password()) {
90         DCHECK(it->extra.get());
91         specifics.reset(new sync_pb::EntitySpecifics(it->specifics));
92         specifics->mutable_password()->mutable_client_only_encrypted_data()->
93             CopyFrom(it->extra->unencrypted());
94       }
95       syncer_changes_.push_back(syncer::SyncChange(
96           FROM_HERE,
97           syncer::SyncChange::ACTION_DELETE,
98           syncer::SyncData::CreateRemoteData(
99               it->id, specifics ? *specifics : it->specifics, base::Time())));
100     } else {
101       syncer::SyncChange::SyncChangeType action =
102           (it->action == syncer::ChangeRecord::ACTION_ADD) ?
103           syncer::SyncChange::ACTION_ADD : syncer::SyncChange::ACTION_UPDATE;
104       // Need to load specifics from node.
105       syncer::ReadNode read_node(trans);
106       if (read_node.InitByIdLookup(it->id) != syncer::BaseNode::INIT_OK) {
107         error_handler()->OnSingleDatatypeUnrecoverableError(
108             FROM_HERE,
109             "Failed to look up data for received change with id " +
110                 base::Int64ToString(it->id));
111         return;
112       }
113       syncer_changes_.push_back(
114           syncer::SyncChange(
115               FROM_HERE,
116               action,
117               BuildRemoteSyncData(it->id, read_node)));
118     }
119   }
120 }
121
122 void GenericChangeProcessor::CommitChangesFromSyncModel() {
123   DCHECK(CalledOnValidThread());
124   if (syncer_changes_.empty())
125     return;
126   if (!local_service_.get()) {
127     syncer::ModelType type = syncer_changes_[0].sync_data().GetDataType();
128     syncer::SyncError error(FROM_HERE,
129                             syncer::SyncError::DATATYPE_ERROR,
130                             "Local service destroyed.",
131                             type);
132     error_handler()->OnSingleDatatypeUnrecoverableError(error.location(),
133                                                         error.message());
134     return;
135   }
136   syncer::SyncError error = local_service_->ProcessSyncChanges(FROM_HERE,
137                                                        syncer_changes_);
138   syncer_changes_.clear();
139   if (error.IsSet()) {
140     error_handler()->OnSingleDatatypeUnrecoverableError(
141         error.location(), error.message());
142   }
143 }
144
145 syncer::SyncDataList GenericChangeProcessor::GetAllSyncData(
146     syncer::ModelType type) const {
147   // This is slow / memory intensive.  Should be used sparingly by datatypes.
148   syncer::SyncDataList data;
149   GetAllSyncDataReturnError(type, &data);
150   return data;
151 }
152
153 syncer::SyncError GenericChangeProcessor::GetAllSyncDataReturnError(
154     syncer::ModelType type,
155     syncer::SyncDataList* current_sync_data) const {
156   DCHECK(CalledOnValidThread());
157   std::string type_name = syncer::ModelTypeToString(type);
158   syncer::ReadTransaction trans(FROM_HERE, share_handle());
159   syncer::ReadNode root(&trans);
160   if (root.InitByTagLookup(syncer::ModelTypeToRootTag(type)) !=
161           syncer::BaseNode::INIT_OK) {
162     syncer::SyncError error(FROM_HERE,
163                             syncer::SyncError::DATATYPE_ERROR,
164                             "Server did not create the top-level " + type_name +
165                                 " node. We might be running against an out-of-"
166                                 "date server.",
167                             type);
168     return error;
169   }
170
171   // TODO(akalin): We'll have to do a tree traversal for bookmarks.
172   DCHECK_NE(type, syncer::BOOKMARKS);
173
174   std::vector<int64> child_ids;
175   root.GetChildIds(&child_ids);
176
177   for (std::vector<int64>::iterator it = child_ids.begin();
178        it != child_ids.end(); ++it) {
179     syncer::ReadNode sync_child_node(&trans);
180     if (sync_child_node.InitByIdLookup(*it) !=
181             syncer::BaseNode::INIT_OK) {
182       syncer::SyncError error(FROM_HERE,
183                               syncer::SyncError::DATATYPE_ERROR,
184                               "Failed to fetch child node for type " +
185                                   type_name + ".",
186                               type);
187       return error;
188     }
189     current_sync_data->push_back(BuildRemoteSyncData(sync_child_node.GetId(),
190                                                      sync_child_node));
191   }
192   return syncer::SyncError();
193 }
194
195 int GenericChangeProcessor::GetSyncCountForType(syncer::ModelType 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     return 0;
201
202   // Subtract one to account for type's root node.
203   return root.GetTotalNodeCount() - 1;
204 }
205
206 namespace {
207
208 // TODO(isherman): Investigating http://crbug.com/121592
209 // WARNING: this code is sensitive to compiler optimizations. Be careful
210 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
211 // the compiler attempts to merge it with other calls, losing useful information
212 // in breakpad uploads.
213 syncer::SyncError LogLookupFailure(
214     syncer::BaseNode::InitByLookupResult lookup_result,
215     const tracked_objects::Location& from_here,
216     const std::string& error_prefix,
217     syncer::ModelType type,
218     DataTypeErrorHandler* error_handler) {
219   switch (lookup_result) {
220     case syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD: {
221       syncer::SyncError error;
222       error.Reset(from_here,
223                   error_prefix +
224                       "could not find entry matching the lookup criteria.",
225                   type);
226       error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
227                                                         error.message());
228       LOG(ERROR) << "Delete: Bad entry.";
229       return error;
230     }
231     case syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL: {
232       syncer::SyncError error;
233       error.Reset(from_here, error_prefix + "entry is already deleted.", type);
234       error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
235                                                         error.message());
236       LOG(ERROR) << "Delete: Deleted entry.";
237       return error;
238     }
239     case syncer::BaseNode::INIT_FAILED_DECRYPT_IF_NECESSARY: {
240       syncer::SyncError error;
241       error.Reset(from_here, error_prefix + "unable to decrypt", type);
242       error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
243                                                         error.message());
244       LOG(ERROR) << "Delete: Undecryptable entry.";
245       return error;
246     }
247     case syncer::BaseNode::INIT_FAILED_PRECONDITION: {
248       syncer::SyncError error;
249       error.Reset(from_here,
250                   error_prefix + "a precondition was not met for calling init.",
251                   type);
252       error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
253                                                         error.message());
254       LOG(ERROR) << "Delete: Failed precondition.";
255       return error;
256     }
257     default: {
258       syncer::SyncError error;
259       // Should have listed all the possible error cases above.
260       error.Reset(from_here, error_prefix + "unknown error", type);
261       error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
262                                                         error.message());
263       LOG(ERROR) << "Delete: Unknown error.";
264       return error;
265     }
266   }
267 }
268
269 syncer::SyncError AttemptDelete(
270     const syncer::SyncChange& change,
271     syncer::ModelType type,
272     const std::string& type_str,
273     syncer::WriteNode* node,
274     DataTypeErrorHandler* error_handler) {
275   DCHECK_EQ(change.change_type(), syncer::SyncChange::ACTION_DELETE);
276   if (change.sync_data().IsLocal()) {
277     const std::string& tag = change.sync_data().GetTag();
278     if (tag.empty()) {
279       syncer::SyncError error(
280           FROM_HERE,
281           syncer::SyncError::DATATYPE_ERROR,
282           "Failed to delete " + type_str + " node. Local data, empty tag. " +
283               change.location().ToString(),
284           type);
285       error_handler->OnSingleDatatypeUnrecoverableError(error.location(),
286                                                         error.message());
287       NOTREACHED();
288       return error;
289     }
290
291     syncer::BaseNode::InitByLookupResult result =
292         node->InitByClientTagLookup(change.sync_data().GetDataType(), tag);
293     if (result != syncer::BaseNode::INIT_OK) {
294       return LogLookupFailure(
295           result, FROM_HERE,
296           "Failed to delete " + type_str + " node. Local data. " +
297               change.location().ToString(),
298           type, error_handler);
299     }
300   } else {
301     syncer::BaseNode::InitByLookupResult result =
302         node->InitByIdLookup(change.sync_data().GetRemoteId());
303     if (result != syncer::BaseNode::INIT_OK) {
304       return LogLookupFailure(
305           result, FROM_HERE,
306           "Failed to delete " + type_str + " node. Non-local data. " +
307               change.location().ToString(),
308           type, error_handler);
309     }
310   }
311   if (IsActOnceDataType(type))
312     node->Drop();
313   else
314     node->Tombstone();
315   return syncer::SyncError();
316 }
317
318 }  // namespace
319
320 syncer::SyncError GenericChangeProcessor::ProcessSyncChanges(
321     const tracked_objects::Location& from_here,
322     const syncer::SyncChangeList& list_of_changes) {
323   DCHECK(CalledOnValidThread());
324   syncer::WriteTransaction trans(from_here, share_handle());
325
326   for (syncer::SyncChangeList::const_iterator iter = list_of_changes.begin();
327        iter != list_of_changes.end();
328        ++iter) {
329     const syncer::SyncChange& change = *iter;
330     DCHECK_NE(change.sync_data().GetDataType(), syncer::UNSPECIFIED);
331     syncer::ModelType type = change.sync_data().GetDataType();
332     std::string type_str = syncer::ModelTypeToString(type);
333     syncer::WriteNode sync_node(&trans);
334     if (change.change_type() == syncer::SyncChange::ACTION_DELETE) {
335       syncer::SyncError error =
336           AttemptDelete(change, type, type_str, &sync_node, error_handler());
337       if (error.IsSet()) {
338         NOTREACHED();
339         return error;
340       }
341       if (merge_result_.get()) {
342         merge_result_->set_num_items_deleted(
343             merge_result_->num_items_deleted() + 1);
344       }
345     } else if (change.change_type() == syncer::SyncChange::ACTION_ADD) {
346       syncer::SyncError error =
347           HandleActionAdd(change, type_str, type, trans, &sync_node);
348       if (error.IsSet()) {
349         return error;
350       }
351     } else if (change.change_type() == syncer::SyncChange::ACTION_UPDATE) {
352       syncer::SyncError error =
353           HandleActionUpdate(change, type_str, type, trans, &sync_node);
354       if (error.IsSet()) {
355         return error;
356       }
357     } else {
358       syncer::SyncError error(
359           FROM_HERE,
360           syncer::SyncError::DATATYPE_ERROR,
361           "Received unset SyncChange in the change processor, " +
362               change.location().ToString(),
363           type);
364       error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
365                                                           error.message());
366       NOTREACHED();
367       LOG(ERROR) << "Unset sync change.";
368       return error;
369     }
370   }
371   return syncer::SyncError();
372 }
373
374 // WARNING: this code is sensitive to compiler optimizations. Be careful
375 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
376 // the compiler attempts to merge it with other calls, losing useful information
377 // in breakpad uploads.
378 syncer::SyncError GenericChangeProcessor::HandleActionAdd(
379     const syncer::SyncChange& change,
380     const std::string& type_str,
381     const syncer::ModelType& type,
382     const syncer::WriteTransaction& trans,
383     syncer::WriteNode* sync_node) {
384   // TODO(sync): Handle other types of creation (custom parents, folders,
385   // etc.).
386   syncer::ReadNode root_node(&trans);
387   if (root_node.InitByTagLookup(syncer::ModelTypeToRootTag(
388           change.sync_data().GetDataType())) != syncer::BaseNode::INIT_OK) {
389     syncer::SyncError error(FROM_HERE,
390                             syncer::SyncError::DATATYPE_ERROR,
391                             "Failed to look up root node for type " + type_str,
392                             type);
393     error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
394                                                         error.message());
395     NOTREACHED();
396     LOG(ERROR) << "Create: no root node.";
397     return error;
398   }
399   syncer::WriteNode::InitUniqueByCreationResult result =
400       sync_node->InitUniqueByCreation(change.sync_data().GetDataType(),
401                                       root_node,
402                                       change.sync_data().GetTag());
403   if (result != syncer::WriteNode::INIT_SUCCESS) {
404     std::string error_prefix = "Failed to create " + type_str + " node: " +
405                                change.location().ToString() + ", ";
406     switch (result) {
407       case syncer::WriteNode::INIT_FAILED_EMPTY_TAG: {
408         syncer::SyncError error;
409         error.Reset(FROM_HERE, error_prefix + "empty tag", type);
410         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
411                                                             error.message());
412         LOG(ERROR) << "Create: Empty tag.";
413         return error;
414       }
415       case syncer::WriteNode::INIT_FAILED_ENTRY_ALREADY_EXISTS: {
416         syncer::SyncError error;
417         error.Reset(FROM_HERE, error_prefix + "entry already exists", type);
418         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
419                                                             error.message());
420         LOG(ERROR) << "Create: Entry exists.";
421         return error;
422       }
423       case syncer::WriteNode::INIT_FAILED_COULD_NOT_CREATE_ENTRY: {
424         syncer::SyncError error;
425         error.Reset(FROM_HERE, error_prefix + "failed to create entry", type);
426         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
427                                                             error.message());
428         LOG(ERROR) << "Create: Could not create entry.";
429         return error;
430       }
431       case syncer::WriteNode::INIT_FAILED_SET_PREDECESSOR: {
432         syncer::SyncError error;
433         error.Reset(
434             FROM_HERE, error_prefix + "failed to set predecessor", type);
435         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
436                                                             error.message());
437         LOG(ERROR) << "Create: Bad predecessor.";
438         return error;
439       }
440       default: {
441         syncer::SyncError error;
442         error.Reset(FROM_HERE, error_prefix + "unknown error", type);
443         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
444                                                             error.message());
445         LOG(ERROR) << "Create: Unknown error.";
446         return error;
447       }
448     }
449   }
450   sync_node->SetTitle(base::UTF8ToWide(change.sync_data().GetTitle()));
451   SetNodeSpecifics(change.sync_data().GetSpecifics(), sync_node);
452   if (merge_result_.get()) {
453     merge_result_->set_num_items_added(merge_result_->num_items_added() + 1);
454   }
455   return syncer::SyncError();
456 }
457 // WARNING: this code is sensitive to compiler optimizations. Be careful
458 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
459 // the compiler attempts to merge it with other calls, losing useful information
460 // in breakpad uploads.
461 syncer::SyncError GenericChangeProcessor::HandleActionUpdate(
462     const syncer::SyncChange& change,
463     const std::string& type_str,
464     const syncer::ModelType& type,
465     const syncer::WriteTransaction& trans,
466     syncer::WriteNode* sync_node) {
467   // TODO(zea): consider having this logic for all possible changes?
468   syncer::BaseNode::InitByLookupResult result =
469       sync_node->InitByClientTagLookup(change.sync_data().GetDataType(),
470                                        change.sync_data().GetTag());
471   if (result != syncer::BaseNode::INIT_OK) {
472     std::string error_prefix = "Failed to load " + type_str + " node. " +
473                                change.location().ToString() + ", ";
474     if (result == syncer::BaseNode::INIT_FAILED_PRECONDITION) {
475       syncer::SyncError error;
476       error.Reset(FROM_HERE, error_prefix + "empty tag", type);
477       error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
478                                                           error.message());
479       LOG(ERROR) << "Update: Empty tag.";
480       return error;
481     } else if (result == syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD) {
482       syncer::SyncError error;
483       error.Reset(FROM_HERE, error_prefix + "bad entry", type);
484       error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
485                                                           error.message());
486       LOG(ERROR) << "Update: bad entry.";
487       return error;
488     } else if (result == syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL) {
489       syncer::SyncError error;
490       error.Reset(FROM_HERE, error_prefix + "deleted entry", type);
491       error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
492                                                           error.message());
493       LOG(ERROR) << "Update: deleted entry.";
494       return error;
495     } else {
496       syncer::Cryptographer* crypto = trans.GetCryptographer();
497       syncer::ModelTypeSet encrypted_types(trans.GetEncryptedTypes());
498       const sync_pb::EntitySpecifics& specifics =
499           sync_node->GetEntry()->GetSpecifics();
500       CHECK(specifics.has_encrypted());
501       const bool can_decrypt = crypto->CanDecrypt(specifics.encrypted());
502       const bool agreement = encrypted_types.Has(type);
503       if (!agreement && !can_decrypt) {
504         syncer::SyncError error;
505         error.Reset(FROM_HERE,
506                     "Failed to load encrypted entry, missing key and "
507                     "nigori mismatch for " +
508                         type_str + ".",
509                     type);
510         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
511                                                             error.message());
512         LOG(ERROR) << "Update: encr case 1.";
513         return error;
514       } else if (agreement && can_decrypt) {
515         syncer::SyncError error;
516         error.Reset(FROM_HERE,
517                     "Failed to load encrypted entry, we have the key "
518                     "and the nigori matches (?!) for " +
519                         type_str + ".",
520                     type);
521         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
522                                                             error.message());
523         LOG(ERROR) << "Update: encr case 2.";
524         return error;
525       } else if (agreement) {
526         syncer::SyncError error;
527         error.Reset(FROM_HERE,
528                     "Failed to load encrypted entry, missing key and "
529                     "the nigori matches for " +
530                         type_str + ".",
531                     type);
532         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
533                                                             error.message());
534         LOG(ERROR) << "Update: encr case 3.";
535         return error;
536       } else {
537         syncer::SyncError error;
538         error.Reset(FROM_HERE,
539                     "Failed to load encrypted entry, we have the key"
540                     "(?!) and nigori mismatch for " +
541                         type_str + ".",
542                     type);
543         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
544                                                             error.message());
545         LOG(ERROR) << "Update: encr case 4.";
546         return error;
547       }
548     }
549   }
550
551   sync_node->SetTitle(base::UTF8ToWide(change.sync_data().GetTitle()));
552   SetNodeSpecifics(change.sync_data().GetSpecifics(), sync_node);
553   if (merge_result_.get()) {
554     merge_result_->set_num_items_modified(merge_result_->num_items_modified() +
555                                           1);
556   }
557   // TODO(sync): Support updating other parts of the sync node (title,
558   // successor, parent, etc.).
559   return syncer::SyncError();
560 }
561
562 bool GenericChangeProcessor::SyncModelHasUserCreatedNodes(
563     syncer::ModelType type,
564     bool* has_nodes) {
565   DCHECK(CalledOnValidThread());
566   DCHECK(has_nodes);
567   DCHECK_NE(type, syncer::UNSPECIFIED);
568   std::string type_name = syncer::ModelTypeToString(type);
569   std::string err_str = "Server did not create the top-level " + type_name +
570       " node. We might be running against an out-of-date server.";
571   *has_nodes = false;
572   syncer::ReadTransaction trans(FROM_HERE, share_handle());
573   syncer::ReadNode type_root_node(&trans);
574   if (type_root_node.InitByTagLookup(syncer::ModelTypeToRootTag(type)) !=
575           syncer::BaseNode::INIT_OK) {
576     LOG(ERROR) << err_str;
577     return false;
578   }
579
580   // The sync model has user created nodes if the type's root node has any
581   // children.
582   *has_nodes = type_root_node.HasChildren();
583   return true;
584 }
585
586 bool GenericChangeProcessor::CryptoReadyIfNecessary(syncer::ModelType type) {
587   DCHECK(CalledOnValidThread());
588   DCHECK_NE(type, syncer::UNSPECIFIED);
589   // We only access the cryptographer while holding a transaction.
590   syncer::ReadTransaction trans(FROM_HERE, share_handle());
591   const syncer::ModelTypeSet encrypted_types = trans.GetEncryptedTypes();
592   return !encrypted_types.Has(type) ||
593          trans.GetCryptographer()->is_ready();
594 }
595
596 void GenericChangeProcessor::StartImpl(Profile* profile) {
597   DCHECK(CalledOnValidThread());
598 }
599
600 syncer::UserShare* GenericChangeProcessor::share_handle() const {
601   DCHECK(CalledOnValidThread());
602   return share_handle_;
603 }
604
605 }  // namespace browser_sync