Home | History | Annotate | Download | only in sync_driver
      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.
      4 
      5 #include "components/sync_driver/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 "components/sync_driver/sync_api_component_factory.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 namespace browser_sync {
     24 
     25 namespace {
     26 
     27 const int kContextSizeLimit = 1024;  // Datatype context size limit.
     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 // Helper function to convert AttachmentId to AttachmentMetadataRecord.
     41 sync_pb::AttachmentMetadataRecord AttachmentIdToRecord(
     42     const syncer::AttachmentId& attachment_id) {
     43   sync_pb::AttachmentMetadataRecord record;
     44   *record.mutable_id() = attachment_id.GetProto();
     45   return record;
     46 }
     47 
     48 // Replace |write_nodes|'s attachment ids with |attachment_ids|.
     49 void SetAttachmentMetadata(const syncer::AttachmentIdList& attachment_ids,
     50                            syncer::WriteNode* write_node) {
     51   DCHECK(write_node);
     52   sync_pb::AttachmentMetadata attachment_metadata;
     53   std::transform(
     54       attachment_ids.begin(),
     55       attachment_ids.end(),
     56       RepeatedFieldBackInserter(attachment_metadata.mutable_record()),
     57       AttachmentIdToRecord);
     58   write_node->SetAttachmentMetadata(attachment_metadata);
     59 }
     60 
     61 syncer::SyncData BuildRemoteSyncData(
     62     int64 sync_id,
     63     const syncer::BaseNode& read_node,
     64     const syncer::AttachmentServiceProxy& attachment_service_proxy) {
     65   const syncer::AttachmentIdList& attachment_ids = read_node.GetAttachmentIds();
     66   // Use the specifics of non-password datatypes directly (encryption has
     67   // already been handled).
     68   if (read_node.GetModelType() != syncer::PASSWORDS) {
     69     return syncer::SyncData::CreateRemoteData(sync_id,
     70                                               read_node.GetEntitySpecifics(),
     71                                               read_node.GetModificationTime(),
     72                                               attachment_ids,
     73                                               attachment_service_proxy);
     74   }
     75 
     76   // Passwords must be accessed differently, to account for their encryption,
     77   // and stored into a temporary EntitySpecifics.
     78   sync_pb::EntitySpecifics password_holder;
     79   password_holder.mutable_password()->mutable_client_only_encrypted_data()->
     80       CopyFrom(read_node.GetPasswordSpecifics());
     81   return syncer::SyncData::CreateRemoteData(sync_id,
     82                                             password_holder,
     83                                             read_node.GetModificationTime(),
     84                                             attachment_ids,
     85                                             attachment_service_proxy);
     86 }
     87 
     88 }  // namespace
     89 
     90 GenericChangeProcessor::GenericChangeProcessor(
     91     DataTypeErrorHandler* error_handler,
     92     const base::WeakPtr<syncer::SyncableService>& local_service,
     93     const base::WeakPtr<syncer::SyncMergeResult>& merge_result,
     94     syncer::UserShare* user_share,
     95     SyncApiComponentFactory* sync_factory)
     96     : ChangeProcessor(error_handler),
     97       local_service_(local_service),
     98       merge_result_(merge_result),
     99       share_handle_(user_share),
    100       attachment_service_(sync_factory->CreateAttachmentService(this)),
    101       attachment_service_weak_ptr_factory_(attachment_service_.get()),
    102       attachment_service_proxy_(
    103           base::MessageLoopProxy::current(),
    104           attachment_service_weak_ptr_factory_.GetWeakPtr()) {
    105   DCHECK(CalledOnValidThread());
    106   DCHECK(attachment_service_);
    107 }
    108 
    109 GenericChangeProcessor::~GenericChangeProcessor() {
    110   DCHECK(CalledOnValidThread());
    111 }
    112 
    113 void GenericChangeProcessor::ApplyChangesFromSyncModel(
    114     const syncer::BaseTransaction* trans,
    115     int64 model_version,
    116     const syncer::ImmutableChangeRecordList& changes) {
    117   DCHECK(CalledOnValidThread());
    118   DCHECK(syncer_changes_.empty());
    119   for (syncer::ChangeRecordList::const_iterator it =
    120            changes.Get().begin(); it != changes.Get().end(); ++it) {
    121     if (it->action == syncer::ChangeRecord::ACTION_DELETE) {
    122       scoped_ptr<sync_pb::EntitySpecifics> specifics;
    123       if (it->specifics.has_password()) {
    124         DCHECK(it->extra.get());
    125         specifics.reset(new sync_pb::EntitySpecifics(it->specifics));
    126         specifics->mutable_password()->mutable_client_only_encrypted_data()->
    127             CopyFrom(it->extra->unencrypted());
    128       }
    129       const syncer::AttachmentIdList empty_list_of_attachment_ids;
    130       syncer_changes_.push_back(
    131           syncer::SyncChange(FROM_HERE,
    132                              syncer::SyncChange::ACTION_DELETE,
    133                              syncer::SyncData::CreateRemoteData(
    134                                  it->id,
    135                                  specifics ? *specifics : it->specifics,
    136                                  base::Time(),
    137                                  empty_list_of_attachment_ids,
    138                                  attachment_service_proxy_)));
    139     } else {
    140       syncer::SyncChange::SyncChangeType action =
    141           (it->action == syncer::ChangeRecord::ACTION_ADD) ?
    142           syncer::SyncChange::ACTION_ADD : syncer::SyncChange::ACTION_UPDATE;
    143       // Need to load specifics from node.
    144       syncer::ReadNode read_node(trans);
    145       if (read_node.InitByIdLookup(it->id) != syncer::BaseNode::INIT_OK) {
    146         error_handler()->OnSingleDatatypeUnrecoverableError(
    147             FROM_HERE,
    148             "Failed to look up data for received change with id " +
    149                 base::Int64ToString(it->id));
    150         return;
    151       }
    152       syncer_changes_.push_back(syncer::SyncChange(
    153           FROM_HERE,
    154           action,
    155           BuildRemoteSyncData(it->id, read_node, attachment_service_proxy_)));
    156     }
    157   }
    158 }
    159 
    160 void GenericChangeProcessor::CommitChangesFromSyncModel() {
    161   DCHECK(CalledOnValidThread());
    162   if (syncer_changes_.empty())
    163     return;
    164   if (!local_service_.get()) {
    165     syncer::ModelType type = syncer_changes_[0].sync_data().GetDataType();
    166     syncer::SyncError error(FROM_HERE,
    167                             syncer::SyncError::DATATYPE_ERROR,
    168                             "Local service destroyed.",
    169                             type);
    170     error_handler()->OnSingleDatatypeUnrecoverableError(error.location(),
    171                                                         error.message());
    172     return;
    173   }
    174   syncer::SyncError error = local_service_->ProcessSyncChanges(FROM_HERE,
    175                                                        syncer_changes_);
    176   syncer_changes_.clear();
    177   if (error.IsSet()) {
    178     error_handler()->OnSingleDatatypeUnrecoverableError(
    179         error.location(), error.message());
    180   }
    181 }
    182 
    183 syncer::SyncDataList GenericChangeProcessor::GetAllSyncData(
    184     syncer::ModelType type) const {
    185   // This is slow / memory intensive.  Should be used sparingly by datatypes.
    186   syncer::SyncDataList data;
    187   GetAllSyncDataReturnError(type, &data);
    188   return data;
    189 }
    190 
    191 syncer::SyncError GenericChangeProcessor::UpdateDataTypeContext(
    192     syncer::ModelType type,
    193     syncer::SyncChangeProcessor::ContextRefreshStatus refresh_status,
    194     const std::string& context) {
    195   DCHECK(syncer::ProtocolTypes().Has(type));
    196 
    197   if (context.size() > static_cast<size_t>(kContextSizeLimit)) {
    198     return syncer::SyncError(FROM_HERE,
    199                              syncer::SyncError::DATATYPE_ERROR,
    200                              "Context size limit exceeded.",
    201                              type);
    202   }
    203 
    204   syncer::WriteTransaction trans(FROM_HERE, share_handle());
    205   trans.SetDataTypeContext(type, refresh_status, context);
    206 
    207   // TODO(zea): plumb a pointer to the PSS or SyncManagerImpl here so we can
    208   // trigger a datatype nudge if |refresh_status == REFRESH_NEEDED|.
    209 
    210   return syncer::SyncError();
    211 }
    212 
    213 void GenericChangeProcessor::OnAttachmentUploaded(
    214     const syncer::AttachmentId& attachment_id) {
    215   syncer::WriteTransaction trans(FROM_HERE, share_handle());
    216   trans.UpdateEntriesWithAttachmentId(attachment_id);
    217 }
    218 
    219 syncer::SyncError GenericChangeProcessor::GetAllSyncDataReturnError(
    220     syncer::ModelType type,
    221     syncer::SyncDataList* current_sync_data) const {
    222   DCHECK(CalledOnValidThread());
    223   std::string type_name = syncer::ModelTypeToString(type);
    224   syncer::ReadTransaction trans(FROM_HERE, share_handle());
    225   syncer::ReadNode root(&trans);
    226   if (root.InitTypeRoot(type) != syncer::BaseNode::INIT_OK) {
    227     syncer::SyncError error(FROM_HERE,
    228                             syncer::SyncError::DATATYPE_ERROR,
    229                             "Server did not create the top-level " + type_name +
    230                                 " node. We might be running against an out-of-"
    231                                 "date server.",
    232                             type);
    233     return error;
    234   }
    235 
    236   // TODO(akalin): We'll have to do a tree traversal for bookmarks.
    237   DCHECK_NE(type, syncer::BOOKMARKS);
    238 
    239   std::vector<int64> child_ids;
    240   root.GetChildIds(&child_ids);
    241 
    242   for (std::vector<int64>::iterator it = child_ids.begin();
    243        it != child_ids.end(); ++it) {
    244     syncer::ReadNode sync_child_node(&trans);
    245     if (sync_child_node.InitByIdLookup(*it) !=
    246             syncer::BaseNode::INIT_OK) {
    247       syncer::SyncError error(FROM_HERE,
    248                               syncer::SyncError::DATATYPE_ERROR,
    249                               "Failed to fetch child node for type " +
    250                                   type_name + ".",
    251                               type);
    252       return error;
    253     }
    254     current_sync_data->push_back(BuildRemoteSyncData(
    255         sync_child_node.GetId(), sync_child_node, attachment_service_proxy_));
    256   }
    257   return syncer::SyncError();
    258 }
    259 
    260 bool GenericChangeProcessor::GetDataTypeContext(syncer::ModelType type,
    261                                                 std::string* context) const {
    262   syncer::ReadTransaction trans(FROM_HERE, share_handle());
    263   sync_pb::DataTypeContext context_proto;
    264   trans.GetDataTypeContext(type, &context_proto);
    265   if (!context_proto.has_context())
    266     return false;
    267 
    268   DCHECK_EQ(type,
    269             syncer::GetModelTypeFromSpecificsFieldNumber(
    270                 context_proto.data_type_id()));
    271   *context = context_proto.context();
    272   return true;
    273 }
    274 
    275 int GenericChangeProcessor::GetSyncCountForType(syncer::ModelType type) {
    276   syncer::ReadTransaction trans(FROM_HERE, share_handle());
    277   syncer::ReadNode root(&trans);
    278   if (root.InitTypeRoot(type) != syncer::BaseNode::INIT_OK)
    279     return 0;
    280 
    281   // Subtract one to account for type's root node.
    282   return root.GetTotalNodeCount() - 1;
    283 }
    284 
    285 namespace {
    286 
    287 // TODO(isherman): Investigating http://crbug.com/121592
    288 // WARNING: this code is sensitive to compiler optimizations. Be careful
    289 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
    290 // the compiler attempts to merge it with other calls, losing useful information
    291 // in breakpad uploads.
    292 syncer::SyncError LogLookupFailure(
    293     syncer::BaseNode::InitByLookupResult lookup_result,
    294     const tracked_objects::Location& from_here,
    295     const std::string& error_prefix,
    296     syncer::ModelType type,
    297     DataTypeErrorHandler* error_handler) {
    298   switch (lookup_result) {
    299     case syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD: {
    300       syncer::SyncError error;
    301       error.Reset(from_here,
    302                   error_prefix +
    303                       "could not find entry matching the lookup criteria.",
    304                   type);
    305       error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    306                                                         error.message());
    307       LOG(ERROR) << "Delete: Bad entry.";
    308       return error;
    309     }
    310     case syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL: {
    311       syncer::SyncError error;
    312       error.Reset(from_here, error_prefix + "entry is already deleted.", type);
    313       error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    314                                                         error.message());
    315       LOG(ERROR) << "Delete: Deleted entry.";
    316       return error;
    317     }
    318     case syncer::BaseNode::INIT_FAILED_DECRYPT_IF_NECESSARY: {
    319       syncer::SyncError error;
    320       error.Reset(from_here, error_prefix + "unable to decrypt", type);
    321       error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    322                                                         error.message());
    323       LOG(ERROR) << "Delete: Undecryptable entry.";
    324       return error;
    325     }
    326     case syncer::BaseNode::INIT_FAILED_PRECONDITION: {
    327       syncer::SyncError error;
    328       error.Reset(from_here,
    329                   error_prefix + "a precondition was not met for calling init.",
    330                   type);
    331       error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    332                                                         error.message());
    333       LOG(ERROR) << "Delete: Failed precondition.";
    334       return error;
    335     }
    336     default: {
    337       syncer::SyncError error;
    338       // Should have listed all the possible error cases above.
    339       error.Reset(from_here, error_prefix + "unknown error", type);
    340       error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    341                                                         error.message());
    342       LOG(ERROR) << "Delete: Unknown error.";
    343       return error;
    344     }
    345   }
    346 }
    347 
    348 syncer::SyncError AttemptDelete(const syncer::SyncChange& change,
    349                                 syncer::ModelType type,
    350                                 const std::string& type_str,
    351                                 syncer::WriteNode* node,
    352                                 DataTypeErrorHandler* error_handler) {
    353   DCHECK_EQ(change.change_type(), syncer::SyncChange::ACTION_DELETE);
    354   if (change.sync_data().IsLocal()) {
    355     const std::string& tag = syncer::SyncDataLocal(change.sync_data()).GetTag();
    356     if (tag.empty()) {
    357       syncer::SyncError error(
    358           FROM_HERE,
    359           syncer::SyncError::DATATYPE_ERROR,
    360           "Failed to delete " + type_str + " node. Local data, empty tag. " +
    361               change.location().ToString(),
    362           type);
    363       error_handler->OnSingleDatatypeUnrecoverableError(error.location(),
    364                                                         error.message());
    365       NOTREACHED();
    366       return error;
    367     }
    368 
    369     syncer::BaseNode::InitByLookupResult result =
    370         node->InitByClientTagLookup(change.sync_data().GetDataType(), tag);
    371     if (result != syncer::BaseNode::INIT_OK) {
    372       return LogLookupFailure(
    373           result, FROM_HERE,
    374           "Failed to delete " + type_str + " node. Local data. " +
    375               change.location().ToString(),
    376           type, error_handler);
    377     }
    378   } else {
    379     syncer::BaseNode::InitByLookupResult result = node->InitByIdLookup(
    380         syncer::SyncDataRemote(change.sync_data()).GetId());
    381     if (result != syncer::BaseNode::INIT_OK) {
    382       return LogLookupFailure(
    383           result, FROM_HERE,
    384           "Failed to delete " + type_str + " node. Non-local data. " +
    385               change.location().ToString(),
    386           type, error_handler);
    387     }
    388   }
    389   if (IsActOnceDataType(type))
    390     node->Drop();
    391   else
    392     node->Tombstone();
    393   return syncer::SyncError();
    394 }
    395 
    396 // A callback invoked on completion of AttachmentService::StoreAttachment.
    397 void IgnoreStoreResult(const syncer::AttachmentService::StoreResult&) {
    398   // TODO(maniscalco): Here is where we're going to update the in-directory
    399   // entry to indicate that the attachments have been successfully stored on
    400   // disk.  Why do we care?  Because we might crash after persisting the
    401   // directory to disk, but before we have persisted its attachments, leaving us
    402   // with danging attachment ids.  Having a flag that indicates we've stored the
    403   // entry will allow us to detect and filter entries with dangling attachment
    404   // ids (bug 368353).
    405 }
    406 
    407 void StoreAttachments(syncer::AttachmentService* attachment_service,
    408                       const syncer::AttachmentList& attachments) {
    409   DCHECK(attachment_service);
    410   syncer::AttachmentService::StoreCallback ignore_store_result =
    411       base::Bind(&IgnoreStoreResult);
    412   attachment_service->StoreAttachments(attachments, ignore_store_result);
    413 }
    414 
    415 }  // namespace
    416 
    417 syncer::SyncError GenericChangeProcessor::ProcessSyncChanges(
    418     const tracked_objects::Location& from_here,
    419     const syncer::SyncChangeList& list_of_changes) {
    420   DCHECK(CalledOnValidThread());
    421 
    422   // Keep track of brand new attachments so we can persist them on this device
    423   // and upload them to the server.
    424   syncer::AttachmentList new_attachments;
    425 
    426   syncer::WriteTransaction trans(from_here, share_handle());
    427 
    428   for (syncer::SyncChangeList::const_iterator iter = list_of_changes.begin();
    429        iter != list_of_changes.end();
    430        ++iter) {
    431     const syncer::SyncChange& change = *iter;
    432     DCHECK_NE(change.sync_data().GetDataType(), syncer::UNSPECIFIED);
    433     syncer::ModelType type = change.sync_data().GetDataType();
    434     std::string type_str = syncer::ModelTypeToString(type);
    435     syncer::WriteNode sync_node(&trans);
    436     if (change.change_type() == syncer::SyncChange::ACTION_DELETE) {
    437       syncer::SyncError error =
    438           AttemptDelete(change, type, type_str, &sync_node, error_handler());
    439       if (error.IsSet()) {
    440         NOTREACHED();
    441         return error;
    442       }
    443       if (merge_result_.get()) {
    444         merge_result_->set_num_items_deleted(
    445             merge_result_->num_items_deleted() + 1);
    446       }
    447     } else if (change.change_type() == syncer::SyncChange::ACTION_ADD) {
    448       syncer::SyncError error = HandleActionAdd(
    449           change, type_str, type, trans, &sync_node, &new_attachments);
    450       if (error.IsSet()) {
    451         return error;
    452       }
    453     } else if (change.change_type() == syncer::SyncChange::ACTION_UPDATE) {
    454       syncer::SyncError error = HandleActionUpdate(
    455           change, type_str, type, trans, &sync_node, &new_attachments);
    456       if (error.IsSet()) {
    457         return error;
    458       }
    459     } else {
    460       syncer::SyncError error(
    461           FROM_HERE,
    462           syncer::SyncError::DATATYPE_ERROR,
    463           "Received unset SyncChange in the change processor, " +
    464               change.location().ToString(),
    465           type);
    466       error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    467                                                           error.message());
    468       NOTREACHED();
    469       LOG(ERROR) << "Unset sync change.";
    470       return error;
    471     }
    472   }
    473 
    474   if (!new_attachments.empty()) {
    475     StoreAttachments(attachment_service_.get(), new_attachments);
    476   }
    477 
    478   return syncer::SyncError();
    479 }
    480 
    481 // WARNING: this code is sensitive to compiler optimizations. Be careful
    482 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
    483 // the compiler attempts to merge it with other calls, losing useful information
    484 // in breakpad uploads.
    485 syncer::SyncError GenericChangeProcessor::HandleActionAdd(
    486     const syncer::SyncChange& change,
    487     const std::string& type_str,
    488     const syncer::ModelType& type,
    489     const syncer::WriteTransaction& trans,
    490     syncer::WriteNode* sync_node,
    491     syncer::AttachmentList* new_attachments) {
    492   // TODO(sync): Handle other types of creation (custom parents, folders,
    493   // etc.).
    494   syncer::ReadNode root_node(&trans);
    495   const syncer::SyncDataLocal sync_data_local(change.sync_data());
    496   if (root_node.InitTypeRoot(sync_data_local.GetDataType()) !=
    497       syncer::BaseNode::INIT_OK) {
    498     syncer::SyncError error(FROM_HERE,
    499                             syncer::SyncError::DATATYPE_ERROR,
    500                             "Failed to look up root node for type " + type_str,
    501                             type);
    502     error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    503                                                         error.message());
    504     NOTREACHED();
    505     LOG(ERROR) << "Create: no root node.";
    506     return error;
    507   }
    508   syncer::WriteNode::InitUniqueByCreationResult result =
    509       sync_node->InitUniqueByCreation(
    510           sync_data_local.GetDataType(), root_node, sync_data_local.GetTag());
    511   if (result != syncer::WriteNode::INIT_SUCCESS) {
    512     std::string error_prefix = "Failed to create " + type_str + " node: " +
    513                                change.location().ToString() + ", ";
    514     switch (result) {
    515       case syncer::WriteNode::INIT_FAILED_EMPTY_TAG: {
    516         syncer::SyncError error;
    517         error.Reset(FROM_HERE, error_prefix + "empty tag", type);
    518         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    519                                                             error.message());
    520         LOG(ERROR) << "Create: Empty tag.";
    521         return error;
    522       }
    523       case syncer::WriteNode::INIT_FAILED_ENTRY_ALREADY_EXISTS: {
    524         syncer::SyncError error;
    525         error.Reset(FROM_HERE, error_prefix + "entry already exists", type);
    526         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    527                                                             error.message());
    528         LOG(ERROR) << "Create: Entry exists.";
    529         return error;
    530       }
    531       case syncer::WriteNode::INIT_FAILED_COULD_NOT_CREATE_ENTRY: {
    532         syncer::SyncError error;
    533         error.Reset(FROM_HERE, error_prefix + "failed to create entry", type);
    534         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    535                                                             error.message());
    536         LOG(ERROR) << "Create: Could not create entry.";
    537         return error;
    538       }
    539       case syncer::WriteNode::INIT_FAILED_SET_PREDECESSOR: {
    540         syncer::SyncError error;
    541         error.Reset(
    542             FROM_HERE, error_prefix + "failed to set predecessor", type);
    543         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    544                                                             error.message());
    545         LOG(ERROR) << "Create: Bad predecessor.";
    546         return error;
    547       }
    548       default: {
    549         syncer::SyncError error;
    550         error.Reset(FROM_HERE, error_prefix + "unknown error", type);
    551         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    552                                                             error.message());
    553         LOG(ERROR) << "Create: Unknown error.";
    554         return error;
    555       }
    556     }
    557   }
    558   sync_node->SetTitle(change.sync_data().GetTitle());
    559   SetNodeSpecifics(sync_data_local.GetSpecifics(), sync_node);
    560 
    561   syncer::AttachmentIdList attachment_ids = sync_data_local.GetAttachmentIds();
    562   SetAttachmentMetadata(attachment_ids, sync_node);
    563 
    564   // Return any newly added attachments.
    565   const syncer::AttachmentList& local_attachments_for_upload =
    566       sync_data_local.GetLocalAttachmentsForUpload();
    567   new_attachments->insert(new_attachments->end(),
    568                           local_attachments_for_upload.begin(),
    569                           local_attachments_for_upload.end());
    570 
    571   if (merge_result_.get()) {
    572     merge_result_->set_num_items_added(merge_result_->num_items_added() + 1);
    573   }
    574   return syncer::SyncError();
    575 }
    576 // WARNING: this code is sensitive to compiler optimizations. Be careful
    577 // modifying any code around an OnSingleDatatypeUnrecoverableError call, else
    578 // the compiler attempts to merge it with other calls, losing useful information
    579 // in breakpad uploads.
    580 syncer::SyncError GenericChangeProcessor::HandleActionUpdate(
    581     const syncer::SyncChange& change,
    582     const std::string& type_str,
    583     const syncer::ModelType& type,
    584     const syncer::WriteTransaction& trans,
    585     syncer::WriteNode* sync_node,
    586     syncer::AttachmentList* new_attachments) {
    587   // TODO(zea): consider having this logic for all possible changes?
    588 
    589   const syncer::SyncDataLocal sync_data_local(change.sync_data());
    590   syncer::BaseNode::InitByLookupResult result =
    591       sync_node->InitByClientTagLookup(sync_data_local.GetDataType(),
    592                                        sync_data_local.GetTag());
    593   if (result != syncer::BaseNode::INIT_OK) {
    594     std::string error_prefix = "Failed to load " + type_str + " node. " +
    595                                change.location().ToString() + ", ";
    596     if (result == syncer::BaseNode::INIT_FAILED_PRECONDITION) {
    597       syncer::SyncError error;
    598       error.Reset(FROM_HERE, error_prefix + "empty tag", type);
    599       error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    600                                                           error.message());
    601       LOG(ERROR) << "Update: Empty tag.";
    602       return error;
    603     } else if (result == syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD) {
    604       syncer::SyncError error;
    605       error.Reset(FROM_HERE, error_prefix + "bad entry", type);
    606       error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    607                                                           error.message());
    608       LOG(ERROR) << "Update: bad entry.";
    609       return error;
    610     } else if (result == syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL) {
    611       syncer::SyncError error;
    612       error.Reset(FROM_HERE, error_prefix + "deleted entry", type);
    613       error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    614                                                           error.message());
    615       LOG(ERROR) << "Update: deleted entry.";
    616       return error;
    617     } else {
    618       syncer::Cryptographer* crypto = trans.GetCryptographer();
    619       syncer::ModelTypeSet encrypted_types(trans.GetEncryptedTypes());
    620       const sync_pb::EntitySpecifics& specifics =
    621           sync_node->GetEntry()->GetSpecifics();
    622       CHECK(specifics.has_encrypted());
    623       const bool can_decrypt = crypto->CanDecrypt(specifics.encrypted());
    624       const bool agreement = encrypted_types.Has(type);
    625       if (!agreement && !can_decrypt) {
    626         syncer::SyncError error;
    627         error.Reset(FROM_HERE,
    628                     "Failed to load encrypted entry, missing key and "
    629                     "nigori mismatch for " +
    630                         type_str + ".",
    631                     type);
    632         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    633                                                             error.message());
    634         LOG(ERROR) << "Update: encr case 1.";
    635         return error;
    636       } else if (agreement && can_decrypt) {
    637         syncer::SyncError error;
    638         error.Reset(FROM_HERE,
    639                     "Failed to load encrypted entry, we have the key "
    640                     "and the nigori matches (?!) for " +
    641                         type_str + ".",
    642                     type);
    643         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    644                                                             error.message());
    645         LOG(ERROR) << "Update: encr case 2.";
    646         return error;
    647       } else if (agreement) {
    648         syncer::SyncError error;
    649         error.Reset(FROM_HERE,
    650                     "Failed to load encrypted entry, missing key and "
    651                     "the nigori matches for " +
    652                         type_str + ".",
    653                     type);
    654         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    655                                                             error.message());
    656         LOG(ERROR) << "Update: encr case 3.";
    657         return error;
    658       } else {
    659         syncer::SyncError error;
    660         error.Reset(FROM_HERE,
    661                     "Failed to load encrypted entry, we have the key"
    662                     "(?!) and nigori mismatch for " +
    663                         type_str + ".",
    664                     type);
    665         error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
    666                                                             error.message());
    667         LOG(ERROR) << "Update: encr case 4.";
    668         return error;
    669       }
    670     }
    671   }
    672 
    673   sync_node->SetTitle(change.sync_data().GetTitle());
    674   SetNodeSpecifics(sync_data_local.GetSpecifics(), sync_node);
    675   SetAttachmentMetadata(sync_data_local.GetAttachmentIds(), sync_node);
    676 
    677   // Return any newly added attachments.
    678   const syncer::AttachmentList& local_attachments_for_upload =
    679       sync_data_local.GetLocalAttachmentsForUpload();
    680   new_attachments->insert(new_attachments->end(),
    681                           local_attachments_for_upload.begin(),
    682                           local_attachments_for_upload.end());
    683 
    684   if (merge_result_.get()) {
    685     merge_result_->set_num_items_modified(merge_result_->num_items_modified() +
    686                                           1);
    687   }
    688   // TODO(sync): Support updating other parts of the sync node (title,
    689   // successor, parent, etc.).
    690   return syncer::SyncError();
    691 }
    692 
    693 bool GenericChangeProcessor::SyncModelHasUserCreatedNodes(
    694     syncer::ModelType type,
    695     bool* has_nodes) {
    696   DCHECK(CalledOnValidThread());
    697   DCHECK(has_nodes);
    698   DCHECK_NE(type, syncer::UNSPECIFIED);
    699   std::string type_name = syncer::ModelTypeToString(type);
    700   std::string err_str = "Server did not create the top-level " + type_name +
    701       " node. We might be running against an out-of-date server.";
    702   *has_nodes = false;
    703   syncer::ReadTransaction trans(FROM_HERE, share_handle());
    704   syncer::ReadNode type_root_node(&trans);
    705   if (type_root_node.InitTypeRoot(type) != syncer::BaseNode::INIT_OK) {
    706     LOG(ERROR) << err_str;
    707     return false;
    708   }
    709 
    710   // The sync model has user created nodes if the type's root node has any
    711   // children.
    712   *has_nodes = type_root_node.HasChildren();
    713   return true;
    714 }
    715 
    716 bool GenericChangeProcessor::CryptoReadyIfNecessary(syncer::ModelType type) {
    717   DCHECK(CalledOnValidThread());
    718   DCHECK_NE(type, syncer::UNSPECIFIED);
    719   // We only access the cryptographer while holding a transaction.
    720   syncer::ReadTransaction trans(FROM_HERE, share_handle());
    721   const syncer::ModelTypeSet encrypted_types = trans.GetEncryptedTypes();
    722   return !encrypted_types.Has(type) ||
    723          trans.GetCryptographer()->is_ready();
    724 }
    725 
    726 void GenericChangeProcessor::StartImpl() {
    727 }
    728 
    729 syncer::UserShare* GenericChangeProcessor::share_handle() const {
    730   DCHECK(CalledOnValidThread());
    731   return share_handle_;
    732 }
    733 
    734 }  // namespace browser_sync
    735