Home | History | Annotate | Download | only in engine
      1 // Copyright 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 "sync/engine/build_commit_command.h"
      6 
      7 #include <limits>
      8 #include <set>
      9 #include <string>
     10 #include <vector>
     11 
     12 #include "base/strings/string_util.h"
     13 #include "sync/engine/syncer_proto_util.h"
     14 #include "sync/internal_api/public/base/unique_position.h"
     15 #include "sync/protocol/bookmark_specifics.pb.h"
     16 #include "sync/protocol/sync.pb.h"
     17 #include "sync/sessions/ordered_commit_set.h"
     18 #include "sync/sessions/sync_session.h"
     19 #include "sync/syncable/directory.h"
     20 #include "sync/syncable/entry.h"
     21 #include "sync/syncable/syncable_base_transaction.h"
     22 #include "sync/syncable/syncable_changes_version.h"
     23 #include "sync/syncable/syncable_proto_util.h"
     24 #include "sync/util/time.h"
     25 
     26 using std::set;
     27 using std::string;
     28 using std::vector;
     29 
     30 namespace syncer {
     31 
     32 using sessions::SyncSession;
     33 using syncable::Entry;
     34 using syncable::IS_DEL;
     35 using syncable::IS_UNAPPLIED_UPDATE;
     36 using syncable::IS_UNSYNCED;
     37 using syncable::Id;
     38 using syncable::SPECIFICS;
     39 using syncable::UNIQUE_POSITION;
     40 
     41 BuildCommitCommand::BuildCommitCommand(
     42     syncable::BaseTransaction* trans,
     43     const sessions::OrderedCommitSet& batch_commit_set,
     44     sync_pb::ClientToServerMessage* commit_message,
     45     ExtensionsActivity::Records* extensions_activity_buffer)
     46   : trans_(trans),
     47     batch_commit_set_(batch_commit_set),
     48     commit_message_(commit_message),
     49     extensions_activity_buffer_(extensions_activity_buffer) {
     50 }
     51 
     52 BuildCommitCommand::~BuildCommitCommand() {}
     53 
     54 void BuildCommitCommand::AddExtensionsActivityToMessage(
     55     SyncSession* session, sync_pb::CommitMessage* message) {
     56   // We only send ExtensionsActivity to the server if bookmarks are being
     57   // committed.
     58   ExtensionsActivity* activity = session->context()->extensions_activity();
     59   if (batch_commit_set_.HasBookmarkCommitId()) {
     60     // This isn't perfect, since the set of extensions activity may not
     61     // correlate exactly with the items being committed.  That's OK as
     62     // long as we're looking for a rough estimate of extensions activity,
     63     // not an precise mapping of which commits were triggered by which
     64     // extension.
     65     //
     66     // We will push this list of extensions activity back into the
     67     // ExtensionsActivityMonitor if this commit fails.  That's why we must keep
     68     // a copy of these records in the session.
     69     activity->GetAndClearRecords(extensions_activity_buffer_);
     70 
     71     const ExtensionsActivity::Records& records =
     72         *extensions_activity_buffer_;
     73     for (ExtensionsActivity::Records::const_iterator it =
     74          records.begin();
     75          it != records.end(); ++it) {
     76       sync_pb::ChromiumExtensionsActivity* activity_message =
     77           message->add_extensions_activity();
     78       activity_message->set_extension_id(it->second.extension_id);
     79       activity_message->set_bookmark_writes_since_last_commit(
     80           it->second.bookmark_write_count);
     81     }
     82   }
     83 }
     84 
     85 void BuildCommitCommand::AddClientConfigParamsToMessage(
     86     SyncSession* session, sync_pb::CommitMessage* message) {
     87   const ModelSafeRoutingInfo& routing_info = session->context()->routing_info();
     88   sync_pb::ClientConfigParams* config_params = message->mutable_config_params();
     89   for (std::map<ModelType, ModelSafeGroup>::const_iterator iter =
     90           routing_info.begin(); iter != routing_info.end(); ++iter) {
     91     if (ProxyTypes().Has(iter->first))
     92       continue;
     93     int field_number = GetSpecificsFieldNumberFromModelType(iter->first);
     94     config_params->mutable_enabled_type_ids()->Add(field_number);
     95   }
     96   config_params->set_tabs_datatype_enabled(
     97       routing_info.count(syncer::PROXY_TABS) > 0);
     98 }
     99 
    100 namespace {
    101 void SetEntrySpecifics(Entry* meta_entry,
    102                        sync_pb::SyncEntity* sync_entry) {
    103   // Add the new style extension and the folder bit.
    104   sync_entry->mutable_specifics()->CopyFrom(meta_entry->Get(SPECIFICS));
    105   sync_entry->set_folder(meta_entry->Get(syncable::IS_DIR));
    106 
    107   CHECK(!sync_entry->specifics().password().has_client_only_encrypted_data());
    108   DCHECK_EQ(meta_entry->GetModelType(), GetModelType(*sync_entry));
    109 }
    110 }  // namespace
    111 
    112 SyncerError BuildCommitCommand::ExecuteImpl(SyncSession* session) {
    113   commit_message_->set_share(session->context()->account_name());
    114   commit_message_->set_message_contents(sync_pb::ClientToServerMessage::COMMIT);
    115 
    116   sync_pb::CommitMessage* commit_message = commit_message_->mutable_commit();
    117   commit_message->set_cache_guid(trans_->directory()->cache_guid());
    118   AddExtensionsActivityToMessage(session, commit_message);
    119   AddClientConfigParamsToMessage(session, commit_message);
    120 
    121   // Cache previously computed position values.  Because |commit_ids|
    122   // is already in sibling order, we should always hit this map after
    123   // the first sibling in a consecutive run of commit items.  The
    124   // entries in this map are (low, high) values describing the
    125   // space of positions that are immediate successors of the item
    126   // whose ID is the map's key.
    127   std::map<Id, std::pair<int64, int64> > position_map;
    128 
    129   for (size_t i = 0; i < batch_commit_set_.Size(); i++) {
    130     Id id = batch_commit_set_.GetCommitIdAt(i);
    131     sync_pb::SyncEntity* sync_entry = commit_message->add_entries();
    132     sync_entry->set_id_string(SyncableIdToProto(id));
    133     Entry meta_entry(trans_, syncable::GET_BY_ID, id);
    134     CHECK(meta_entry.good());
    135 
    136     DCHECK_NE(0UL,
    137               session->context()->routing_info().count(
    138                   meta_entry.GetModelType()))
    139         << "Committing change to datatype that's not actively enabled.";
    140 
    141     string name = meta_entry.Get(syncable::NON_UNIQUE_NAME);
    142     CHECK(!name.empty());  // Make sure this isn't an update.
    143     // Note: Truncation is also performed in WriteNode::SetTitle(..). But this
    144     // call is still necessary to handle any title changes that might originate
    145     // elsewhere, or already be persisted in the directory.
    146     TruncateUTF8ToByteSize(name, 255, &name);
    147     sync_entry->set_name(name);
    148 
    149     // Set the non_unique_name.  If we do, the server ignores
    150     // the |name| value (using |non_unique_name| instead), and will return
    151     // in the CommitResponse a unique name if one is generated.
    152     // We send both because it may aid in logging.
    153     sync_entry->set_non_unique_name(name);
    154 
    155     if (!meta_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) {
    156       sync_entry->set_client_defined_unique_tag(
    157           meta_entry.Get(syncable::UNIQUE_CLIENT_TAG));
    158     }
    159 
    160     // Deleted items with server-unknown parent ids can be a problem so we set
    161     // the parent to 0. (TODO(sync): Still true in protocol?).
    162     Id new_parent_id;
    163     if (meta_entry.Get(syncable::IS_DEL) &&
    164         !meta_entry.Get(syncable::PARENT_ID).ServerKnows()) {
    165       new_parent_id = trans_->root_id();
    166     } else {
    167       new_parent_id = meta_entry.Get(syncable::PARENT_ID);
    168     }
    169     sync_entry->set_parent_id_string(SyncableIdToProto(new_parent_id));
    170 
    171     // If our parent has changed, send up the old one so the server
    172     // can correctly deal with multiple parents.
    173     // TODO(nick): With the server keeping track of the primary sync parent,
    174     // it should not be necessary to provide the old_parent_id: the version
    175     // number should suffice.
    176     if (new_parent_id != meta_entry.Get(syncable::SERVER_PARENT_ID) &&
    177         0 != meta_entry.Get(syncable::BASE_VERSION) &&
    178         syncable::CHANGES_VERSION != meta_entry.Get(syncable::BASE_VERSION)) {
    179       sync_entry->set_old_parent_id(
    180           SyncableIdToProto(meta_entry.Get(syncable::SERVER_PARENT_ID)));
    181     }
    182 
    183     int64 version = meta_entry.Get(syncable::BASE_VERSION);
    184     if (syncable::CHANGES_VERSION == version || 0 == version) {
    185       // Undeletions are only supported for items that have a client tag.
    186       DCHECK(!id.ServerKnows() ||
    187              !meta_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty())
    188           << meta_entry;
    189 
    190       // Version 0 means to create or undelete an object.
    191       sync_entry->set_version(0);
    192     } else {
    193       DCHECK(id.ServerKnows()) << meta_entry;
    194       sync_entry->set_version(meta_entry.Get(syncable::BASE_VERSION));
    195     }
    196     sync_entry->set_ctime(TimeToProtoTime(meta_entry.Get(syncable::CTIME)));
    197     sync_entry->set_mtime(TimeToProtoTime(meta_entry.Get(syncable::MTIME)));
    198 
    199     // Deletion is final on the server, let's move things and then delete them.
    200     if (meta_entry.Get(IS_DEL)) {
    201       sync_entry->set_deleted(true);
    202     } else {
    203       if (meta_entry.Get(SPECIFICS).has_bookmark()) {
    204         // Both insert_after_item_id and position_in_parent fields are set only
    205         // for legacy reasons.  See comments in sync.proto for more information.
    206         const Id& prev_id = meta_entry.GetPredecessorId();
    207         string prev_id_string =
    208             prev_id.IsRoot() ? string() : prev_id.GetServerId();
    209         sync_entry->set_insert_after_item_id(prev_id_string);
    210         sync_entry->set_position_in_parent(
    211             meta_entry.Get(UNIQUE_POSITION).ToInt64());
    212         meta_entry.Get(UNIQUE_POSITION).ToProto(
    213             sync_entry->mutable_unique_position());
    214       }
    215       SetEntrySpecifics(&meta_entry, sync_entry);
    216     }
    217   }
    218 
    219   return SYNCER_OK;
    220 }
    221 
    222 }  // namespace syncer
    223