Home | History | Annotate | Download | only in engine
      1 // Copyright (c) 2010 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/engine/build_commit_command.h"
      6 
      7 #include <set>
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "base/string_util.h"
     12 #include "chrome/browser/sync/engine/syncer_proto_util.h"
     13 #include "chrome/browser/sync/engine/syncer_util.h"
     14 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
     15 #include "chrome/browser/sync/sessions/sync_session.h"
     16 #include "chrome/browser/sync/syncable/syncable.h"
     17 #include "chrome/browser/sync/syncable/syncable_changes_version.h"
     18 
     19 using std::set;
     20 using std::string;
     21 using std::vector;
     22 using syncable::IS_DEL;
     23 using syncable::Id;
     24 using syncable::MutableEntry;
     25 using syncable::SPECIFICS;
     26 using syncable::UNSPECIFIED;
     27 
     28 namespace browser_sync {
     29 
     30 using sessions::SyncSession;
     31 
     32 BuildCommitCommand::BuildCommitCommand() {}
     33 BuildCommitCommand::~BuildCommitCommand() {}
     34 
     35 void BuildCommitCommand::AddExtensionsActivityToMessage(
     36     SyncSession* session, CommitMessage* message) {
     37   // We only send ExtensionsActivity to the server if bookmarks are being
     38   // committed.
     39   ExtensionsActivityMonitor* monitor = session->context()->extensions_monitor();
     40   if (!session->status_controller()->HasBookmarkCommitActivity()) {
     41     // Return the records to the activity monitor.
     42     monitor->PutRecords(session->extensions_activity());
     43     session->mutable_extensions_activity()->clear();
     44     return;
     45   }
     46   const ExtensionsActivityMonitor::Records& records =
     47       session->extensions_activity();
     48   for (ExtensionsActivityMonitor::Records::const_iterator it = records.begin();
     49        it != records.end(); ++it) {
     50     sync_pb::ChromiumExtensionsActivity* activity_message =
     51         message->add_extensions_activity();
     52     activity_message->set_extension_id(it->second.extension_id);
     53     activity_message->set_bookmark_writes_since_last_commit(
     54         it->second.bookmark_write_count);
     55   }
     56 }
     57 
     58 namespace {
     59 void SetEntrySpecifics(MutableEntry* meta_entry, SyncEntity* sync_entry) {
     60   // Add the new style extension and the folder bit.
     61   sync_entry->mutable_specifics()->CopyFrom(meta_entry->Get(SPECIFICS));
     62   sync_entry->set_folder(meta_entry->Get(syncable::IS_DIR));
     63 
     64   DCHECK(meta_entry->GetModelType() == sync_entry->GetModelType());
     65 }
     66 
     67 void SetOldStyleBookmarkData(MutableEntry* meta_entry, SyncEntity* sync_entry) {
     68   DCHECK(meta_entry->Get(SPECIFICS).HasExtension(sync_pb::bookmark));
     69 
     70   // Old-style inlined bookmark data.
     71   sync_pb::SyncEntity_BookmarkData* bookmark =
     72       sync_entry->mutable_bookmarkdata();
     73 
     74   if (!meta_entry->Get(syncable::IS_DIR)) {
     75     const sync_pb::BookmarkSpecifics& bookmark_specifics =
     76         meta_entry->Get(SPECIFICS).GetExtension(sync_pb::bookmark);
     77     bookmark->set_bookmark_url(bookmark_specifics.url());
     78     bookmark->set_bookmark_favicon(bookmark_specifics.favicon());
     79     bookmark->set_bookmark_folder(false);
     80   } else {
     81     bookmark->set_bookmark_folder(true);
     82   }
     83 }
     84 }  // namespace
     85 
     86 void BuildCommitCommand::ExecuteImpl(SyncSession* session) {
     87   ClientToServerMessage message;
     88   message.set_share(session->context()->account_name());
     89   message.set_message_contents(ClientToServerMessage::COMMIT);
     90 
     91   CommitMessage* commit_message = message.mutable_commit();
     92   commit_message->set_cache_guid(
     93       session->write_transaction()->directory()->cache_guid());
     94   AddExtensionsActivityToMessage(session, commit_message);
     95   SyncerProtoUtil::AddRequestBirthday(
     96       session->write_transaction()->directory(), &message);
     97 
     98   const vector<Id>& commit_ids = session->status_controller()->commit_ids();
     99   for (size_t i = 0; i < commit_ids.size(); i++) {
    100     Id id = commit_ids[i];
    101     SyncEntity* sync_entry =
    102         static_cast<SyncEntity*>(commit_message->add_entries());
    103     sync_entry->set_id(id);
    104     MutableEntry meta_entry(session->write_transaction(),
    105                             syncable::GET_BY_ID,
    106                             id);
    107     CHECK(meta_entry.good());
    108     // This is the only change we make to the entry in this function.
    109     meta_entry.Put(syncable::SYNCING, true);
    110 
    111     DCHECK(0 != session->routing_info().count(meta_entry.GetModelType()))
    112         << "Committing change to datatype that's not actively enabled.";
    113 
    114     string name = meta_entry.Get(syncable::NON_UNIQUE_NAME);
    115     CHECK(!name.empty());  // Make sure this isn't an update.
    116     TruncateUTF8ToByteSize(name, 255, &name);
    117     sync_entry->set_name(name);
    118 
    119     // Set the non_unique_name.  If we do, the server ignores
    120     // the |name| value (using |non_unique_name| instead), and will return
    121     // in the CommitResponse a unique name if one is generated.
    122     // We send both because it may aid in logging.
    123     sync_entry->set_non_unique_name(name);
    124 
    125     if (!meta_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) {
    126       sync_entry->set_client_defined_unique_tag(
    127           meta_entry.Get(syncable::UNIQUE_CLIENT_TAG));
    128     }
    129 
    130     // Deleted items with server-unknown parent ids can be a problem so we set
    131     // the parent to 0. (TODO(sync): Still true in protocol?).
    132     Id new_parent_id;
    133     if (meta_entry.Get(syncable::IS_DEL) &&
    134         !meta_entry.Get(syncable::PARENT_ID).ServerKnows()) {
    135       new_parent_id = session->write_transaction()->root_id();
    136     } else {
    137       new_parent_id = meta_entry.Get(syncable::PARENT_ID);
    138     }
    139     sync_entry->set_parent_id(new_parent_id);
    140 
    141     // If our parent has changed, send up the old one so the server
    142     // can correctly deal with multiple parents.
    143     // TODO(nick): With the server keeping track of the primary sync parent,
    144     // it should not be necessary to provide the old_parent_id: the version
    145     // number should suffice.
    146     if (new_parent_id != meta_entry.Get(syncable::SERVER_PARENT_ID) &&
    147         0 != meta_entry.Get(syncable::BASE_VERSION) &&
    148         syncable::CHANGES_VERSION != meta_entry.Get(syncable::BASE_VERSION)) {
    149       sync_entry->set_old_parent_id(meta_entry.Get(syncable::SERVER_PARENT_ID));
    150     }
    151 
    152     int64 version = meta_entry.Get(syncable::BASE_VERSION);
    153     if (syncable::CHANGES_VERSION == version || 0 == version) {
    154       // Undeletions are only supported for items that have a client tag.
    155       DCHECK(!id.ServerKnows() ||
    156              !meta_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty())
    157           << meta_entry;
    158 
    159       // Version 0 means to create or undelete an object.
    160       sync_entry->set_version(0);
    161     } else {
    162       DCHECK(id.ServerKnows()) << meta_entry;
    163       sync_entry->set_version(meta_entry.Get(syncable::BASE_VERSION));
    164     }
    165     sync_entry->set_ctime(ClientTimeToServerTime(
    166         meta_entry.Get(syncable::CTIME)));
    167     sync_entry->set_mtime(ClientTimeToServerTime(
    168         meta_entry.Get(syncable::MTIME)));
    169 
    170     // Deletion is final on the server, let's move things and then delete them.
    171     if (meta_entry.Get(IS_DEL)) {
    172       sync_entry->set_deleted(true);
    173     } else {
    174       if (meta_entry.Get(SPECIFICS).HasExtension(sync_pb::bookmark)) {
    175         // Common data in both new and old protocol.
    176         const Id& prev_id = meta_entry.Get(syncable::PREV_ID);
    177         string prev_id_string =
    178             prev_id.IsRoot() ? string() : prev_id.GetServerId();
    179         sync_entry->set_insert_after_item_id(prev_id_string);
    180 
    181         // TODO(ncarter): In practice we won't want to send this data twice
    182         // over the wire; instead, when deployed servers are able to accept
    183         // the new-style scheme, we should abandon the old way.
    184         SetOldStyleBookmarkData(&meta_entry, sync_entry);
    185       }
    186       SetEntrySpecifics(&meta_entry, sync_entry);
    187     }
    188   }
    189   session->status_controller()->mutable_commit_message()->CopyFrom(message);
    190 }
    191 
    192 }  // namespace browser_sync
    193