| OLD | NEW |
| (Empty) |
| 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/engine/build_commit_command.h" | |
| 6 | |
| 7 #include <limits> | |
| 8 #include <set> | |
| 9 #include <string> | |
| 10 #include <vector> | |
| 11 | |
| 12 #include "base/string_util.h" | |
| 13 #include "chrome/browser/sync/engine/syncer_proto_util.h" | |
| 14 #include "chrome/browser/sync/sessions/sync_session.h" | |
| 15 #include "chrome/browser/sync/syncable/syncable.h" | |
| 16 #include "chrome/browser/sync/syncable/syncable_changes_version.h" | |
| 17 #include "chrome/browser/sync/util/time.h" | |
| 18 #include "sync/protocol/bookmark_specifics.pb.h" | |
| 19 | |
| 20 using std::set; | |
| 21 using std::string; | |
| 22 using std::vector; | |
| 23 using syncable::Entry; | |
| 24 using syncable::IS_DEL; | |
| 25 using syncable::SERVER_POSITION_IN_PARENT; | |
| 26 using syncable::IS_UNAPPLIED_UPDATE; | |
| 27 using syncable::IS_UNSYNCED; | |
| 28 using syncable::Id; | |
| 29 using syncable::MutableEntry; | |
| 30 using syncable::SPECIFICS; | |
| 31 using syncable::UNSPECIFIED; | |
| 32 | |
| 33 namespace browser_sync { | |
| 34 | |
| 35 using sessions::SyncSession; | |
| 36 | |
| 37 // static | |
| 38 int64 BuildCommitCommand::GetFirstPosition() { | |
| 39 return std::numeric_limits<int64>::min(); | |
| 40 } | |
| 41 | |
| 42 // static | |
| 43 int64 BuildCommitCommand::GetLastPosition() { | |
| 44 return std::numeric_limits<int64>::max(); | |
| 45 } | |
| 46 | |
| 47 // static | |
| 48 int64 BuildCommitCommand::GetGap() { | |
| 49 return 1LL << 20; | |
| 50 } | |
| 51 | |
| 52 BuildCommitCommand::BuildCommitCommand() {} | |
| 53 BuildCommitCommand::~BuildCommitCommand() {} | |
| 54 | |
| 55 void BuildCommitCommand::AddExtensionsActivityToMessage( | |
| 56 SyncSession* session, CommitMessage* message) { | |
| 57 // We only send ExtensionsActivity to the server if bookmarks are being | |
| 58 // committed. | |
| 59 ExtensionsActivityMonitor* monitor = session->context()->extensions_monitor(); | |
| 60 if (!session->status_controller().HasBookmarkCommitActivity()) { | |
| 61 // Return the records to the activity monitor. | |
| 62 monitor->PutRecords(session->extensions_activity()); | |
| 63 session->mutable_extensions_activity()->clear(); | |
| 64 return; | |
| 65 } | |
| 66 const ExtensionsActivityMonitor::Records& records = | |
| 67 session->extensions_activity(); | |
| 68 for (ExtensionsActivityMonitor::Records::const_iterator it = records.begin(); | |
| 69 it != records.end(); ++it) { | |
| 70 sync_pb::ChromiumExtensionsActivity* activity_message = | |
| 71 message->add_extensions_activity(); | |
| 72 activity_message->set_extension_id(it->second.extension_id); | |
| 73 activity_message->set_bookmark_writes_since_last_commit( | |
| 74 it->second.bookmark_write_count); | |
| 75 } | |
| 76 } | |
| 77 | |
| 78 namespace { | |
| 79 void SetEntrySpecifics(MutableEntry* meta_entry, SyncEntity* sync_entry) { | |
| 80 // Add the new style extension and the folder bit. | |
| 81 sync_entry->mutable_specifics()->CopyFrom(meta_entry->Get(SPECIFICS)); | |
| 82 sync_entry->set_folder(meta_entry->Get(syncable::IS_DIR)); | |
| 83 | |
| 84 DCHECK(meta_entry->GetModelType() == sync_entry->GetModelType()); | |
| 85 } | |
| 86 } // namespace | |
| 87 | |
| 88 SyncerError BuildCommitCommand::ExecuteImpl(SyncSession* session) { | |
| 89 ClientToServerMessage message; | |
| 90 message.set_share(session->context()->account_name()); | |
| 91 message.set_message_contents(ClientToServerMessage::COMMIT); | |
| 92 | |
| 93 CommitMessage* commit_message = message.mutable_commit(); | |
| 94 commit_message->set_cache_guid( | |
| 95 session->write_transaction()->directory()->cache_guid()); | |
| 96 AddExtensionsActivityToMessage(session, commit_message); | |
| 97 SyncerProtoUtil::AddRequestBirthday( | |
| 98 session->write_transaction()->directory(), &message); | |
| 99 | |
| 100 // Cache previously computed position values. Because |commit_ids| | |
| 101 // is already in sibling order, we should always hit this map after | |
| 102 // the first sibling in a consecutive run of commit items. The | |
| 103 // entries in this map are (low, high) values describing the | |
| 104 // space of positions that are immediate successors of the item | |
| 105 // whose ID is the map's key. | |
| 106 std::map<Id, std::pair<int64, int64> > position_map; | |
| 107 | |
| 108 const vector<Id>& commit_ids = session->status_controller().commit_ids(); | |
| 109 for (size_t i = 0; i < commit_ids.size(); i++) { | |
| 110 Id id = commit_ids[i]; | |
| 111 SyncEntity* sync_entry = | |
| 112 static_cast<SyncEntity*>(commit_message->add_entries()); | |
| 113 sync_entry->set_id(id); | |
| 114 MutableEntry meta_entry(session->write_transaction(), | |
| 115 syncable::GET_BY_ID, | |
| 116 id); | |
| 117 CHECK(meta_entry.good()); | |
| 118 // This is the only change we make to the entry in this function. | |
| 119 meta_entry.Put(syncable::SYNCING, true); | |
| 120 | |
| 121 DCHECK(0 != session->routing_info().count(meta_entry.GetModelType())) | |
| 122 << "Committing change to datatype that's not actively enabled."; | |
| 123 | |
| 124 string name = meta_entry.Get(syncable::NON_UNIQUE_NAME); | |
| 125 CHECK(!name.empty()); // Make sure this isn't an update. | |
| 126 TruncateUTF8ToByteSize(name, 255, &name); | |
| 127 sync_entry->set_name(name); | |
| 128 | |
| 129 // Set the non_unique_name. If we do, the server ignores | |
| 130 // the |name| value (using |non_unique_name| instead), and will return | |
| 131 // in the CommitResponse a unique name if one is generated. | |
| 132 // We send both because it may aid in logging. | |
| 133 sync_entry->set_non_unique_name(name); | |
| 134 | |
| 135 if (!meta_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) { | |
| 136 sync_entry->set_client_defined_unique_tag( | |
| 137 meta_entry.Get(syncable::UNIQUE_CLIENT_TAG)); | |
| 138 } | |
| 139 | |
| 140 // Deleted items with server-unknown parent ids can be a problem so we set | |
| 141 // the parent to 0. (TODO(sync): Still true in protocol?). | |
| 142 Id new_parent_id; | |
| 143 if (meta_entry.Get(syncable::IS_DEL) && | |
| 144 !meta_entry.Get(syncable::PARENT_ID).ServerKnows()) { | |
| 145 new_parent_id = session->write_transaction()->root_id(); | |
| 146 } else { | |
| 147 new_parent_id = meta_entry.Get(syncable::PARENT_ID); | |
| 148 } | |
| 149 sync_entry->set_parent_id(new_parent_id); | |
| 150 | |
| 151 // If our parent has changed, send up the old one so the server | |
| 152 // can correctly deal with multiple parents. | |
| 153 // TODO(nick): With the server keeping track of the primary sync parent, | |
| 154 // it should not be necessary to provide the old_parent_id: the version | |
| 155 // number should suffice. | |
| 156 if (new_parent_id != meta_entry.Get(syncable::SERVER_PARENT_ID) && | |
| 157 0 != meta_entry.Get(syncable::BASE_VERSION) && | |
| 158 syncable::CHANGES_VERSION != meta_entry.Get(syncable::BASE_VERSION)) { | |
| 159 sync_entry->set_old_parent_id(meta_entry.Get(syncable::SERVER_PARENT_ID)); | |
| 160 } | |
| 161 | |
| 162 int64 version = meta_entry.Get(syncable::BASE_VERSION); | |
| 163 if (syncable::CHANGES_VERSION == version || 0 == version) { | |
| 164 // Undeletions are only supported for items that have a client tag. | |
| 165 DCHECK(!id.ServerKnows() || | |
| 166 !meta_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) | |
| 167 << meta_entry; | |
| 168 | |
| 169 // Version 0 means to create or undelete an object. | |
| 170 sync_entry->set_version(0); | |
| 171 } else { | |
| 172 DCHECK(id.ServerKnows()) << meta_entry; | |
| 173 sync_entry->set_version(meta_entry.Get(syncable::BASE_VERSION)); | |
| 174 } | |
| 175 sync_entry->set_ctime(TimeToProtoTime(meta_entry.Get(syncable::CTIME))); | |
| 176 sync_entry->set_mtime(TimeToProtoTime(meta_entry.Get(syncable::MTIME))); | |
| 177 | |
| 178 // Deletion is final on the server, let's move things and then delete them. | |
| 179 if (meta_entry.Get(IS_DEL)) { | |
| 180 sync_entry->set_deleted(true); | |
| 181 } else { | |
| 182 if (meta_entry.Get(SPECIFICS).has_bookmark()) { | |
| 183 // Common data in both new and old protocol. | |
| 184 const Id& prev_id = meta_entry.Get(syncable::PREV_ID); | |
| 185 string prev_id_string = | |
| 186 prev_id.IsRoot() ? string() : prev_id.GetServerId(); | |
| 187 sync_entry->set_insert_after_item_id(prev_id_string); | |
| 188 | |
| 189 // Compute a numeric position based on what we know locally. | |
| 190 std::pair<int64, int64> position_block( | |
| 191 GetFirstPosition(), GetLastPosition()); | |
| 192 std::map<Id, std::pair<int64, int64> >::iterator prev_pos = | |
| 193 position_map.find(prev_id); | |
| 194 if (prev_pos != position_map.end()) { | |
| 195 position_block = prev_pos->second; | |
| 196 position_map.erase(prev_pos); | |
| 197 } else { | |
| 198 position_block = std::make_pair( | |
| 199 FindAnchorPosition(syncable::PREV_ID, meta_entry), | |
| 200 FindAnchorPosition(syncable::NEXT_ID, meta_entry)); | |
| 201 } | |
| 202 position_block.first = InterpolatePosition(position_block.first, | |
| 203 position_block.second); | |
| 204 | |
| 205 position_map[id] = position_block; | |
| 206 sync_entry->set_position_in_parent(position_block.first); | |
| 207 } | |
| 208 SetEntrySpecifics(&meta_entry, sync_entry); | |
| 209 } | |
| 210 } | |
| 211 session->mutable_status_controller()-> | |
| 212 mutable_commit_message()->CopyFrom(message); | |
| 213 | |
| 214 return SYNCER_OK; | |
| 215 } | |
| 216 | |
| 217 int64 BuildCommitCommand::FindAnchorPosition(syncable::IdField direction, | |
| 218 const syncable::Entry& entry) { | |
| 219 Id next_id = entry.Get(direction); | |
| 220 while (!next_id.IsRoot()) { | |
| 221 Entry next_entry(entry.trans(), | |
| 222 syncable::GET_BY_ID, | |
| 223 next_id); | |
| 224 if (!next_entry.Get(IS_UNSYNCED) && !next_entry.Get(IS_UNAPPLIED_UPDATE)) { | |
| 225 return next_entry.Get(SERVER_POSITION_IN_PARENT); | |
| 226 } | |
| 227 next_id = next_entry.Get(direction); | |
| 228 } | |
| 229 return | |
| 230 direction == syncable::PREV_ID ? | |
| 231 GetFirstPosition() : GetLastPosition(); | |
| 232 } | |
| 233 | |
| 234 int64 BuildCommitCommand::InterpolatePosition(const int64 lo, | |
| 235 const int64 hi) { | |
| 236 DCHECK_LE(lo, hi); | |
| 237 | |
| 238 // The first item to be added under a parent gets a position of zero. | |
| 239 if (lo == GetFirstPosition() && hi == GetLastPosition()) | |
| 240 return 0; | |
| 241 | |
| 242 // For small gaps, we do linear interpolation. For larger gaps, | |
| 243 // we use an additive offset of |GetGap()|. We are careful to avoid | |
| 244 // signed integer overflow. | |
| 245 uint64 delta = static_cast<uint64>(hi) - static_cast<uint64>(lo); | |
| 246 if (delta <= static_cast<uint64>(GetGap()*2)) | |
| 247 return lo + (static_cast<int64>(delta) + 7) / 8; // Interpolate. | |
| 248 else if (lo == GetFirstPosition()) | |
| 249 return hi - GetGap(); // Extend range just before successor. | |
| 250 else | |
| 251 return lo + GetGap(); // Use or extend range just after predecessor. | |
| 252 } | |
| 253 | |
| 254 | |
| 255 } // namespace browser_sync | |
| OLD | NEW |