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