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/syncable/mutable_entry.h" 6 7 #include "base/memory/scoped_ptr.h" 8 #include "sync/internal_api/public/base/unique_position.h" 9 #include "sync/syncable/directory.h" 10 #include "sync/syncable/scoped_kernel_lock.h" 11 #include "sync/syncable/scoped_parent_child_index_updater.h" 12 #include "sync/syncable/syncable-inl.h" 13 #include "sync/syncable/syncable_changes_version.h" 14 #include "sync/syncable/syncable_util.h" 15 #include "sync/syncable/syncable_write_transaction.h" 16 17 using std::string; 18 19 namespace syncer { 20 namespace syncable { 21 22 void MutableEntry::Init(WriteTransaction* trans, 23 ModelType model_type, 24 const Id& parent_id, 25 const string& name) { 26 scoped_ptr<EntryKernel> kernel(new EntryKernel); 27 kernel_ = NULL; 28 29 kernel->put(ID, trans->directory_->NextId()); 30 kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); 31 kernel->mark_dirty(&trans->directory_->kernel_->dirty_metahandles); 32 kernel->put(PARENT_ID, parent_id); 33 kernel->put(NON_UNIQUE_NAME, name); 34 const base::Time& now = base::Time::Now(); 35 kernel->put(CTIME, now); 36 kernel->put(MTIME, now); 37 // We match the database defaults here 38 kernel->put(BASE_VERSION, CHANGES_VERSION); 39 40 // Normally the SPECIFICS setting code is wrapped in logic to deal with 41 // unknown fields and encryption. Since all we want to do here is ensure that 42 // GetModelType() returns a correct value from the very beginning, these 43 // few lines are sufficient. 44 sync_pb::EntitySpecifics specifics; 45 AddDefaultFieldValue(model_type, &specifics); 46 kernel->put(SPECIFICS, specifics); 47 48 // Because this entry is new, it was originally deleted. 49 kernel->put(IS_DEL, true); 50 trans->TrackChangesTo(kernel.get()); 51 kernel->put(IS_DEL, false); 52 53 // Now swap the pointers. 54 kernel_ = kernel.release(); 55 } 56 57 MutableEntry::MutableEntry(WriteTransaction* trans, 58 Create, 59 ModelType model_type, 60 const Id& parent_id, 61 const string& name) 62 : ModelNeutralMutableEntry(trans), write_transaction_(trans) { 63 Init(trans, model_type, parent_id, name); 64 // We need to have a valid position ready before we can index the item. 65 if (model_type == BOOKMARKS) { 66 // Base the tag off of our cache-guid and local "c-" style ID. 67 std::string unique_tag = syncable::GenerateSyncableBookmarkHash( 68 trans->directory()->cache_guid(), GetId().GetServerId()); 69 kernel_->put(UNIQUE_BOOKMARK_TAG, unique_tag); 70 kernel_->put(UNIQUE_POSITION, UniquePosition::InitialPosition(unique_tag)); 71 } else { 72 DCHECK(!ShouldMaintainPosition()); 73 } 74 75 bool result = trans->directory()->InsertEntry(trans, kernel_); 76 DCHECK(result); 77 } 78 79 MutableEntry::MutableEntry(WriteTransaction* trans, CreateNewUpdateItem, 80 const Id& id) 81 : ModelNeutralMutableEntry(trans, CREATE_NEW_UPDATE_ITEM, id), 82 write_transaction_(trans) {} 83 84 MutableEntry::MutableEntry(WriteTransaction* trans, GetById, const Id& id) 85 : ModelNeutralMutableEntry(trans, GET_BY_ID, id), 86 write_transaction_(trans) { 87 } 88 89 MutableEntry::MutableEntry(WriteTransaction* trans, GetByHandle, 90 int64 metahandle) 91 : ModelNeutralMutableEntry(trans, GET_BY_HANDLE, metahandle), 92 write_transaction_(trans) { 93 } 94 95 MutableEntry::MutableEntry(WriteTransaction* trans, GetByClientTag, 96 const std::string& tag) 97 : ModelNeutralMutableEntry(trans, GET_BY_CLIENT_TAG, tag), 98 write_transaction_(trans) { 99 } 100 101 MutableEntry::MutableEntry(WriteTransaction* trans, GetByServerTag, 102 const string& tag) 103 : ModelNeutralMutableEntry(trans, GET_BY_SERVER_TAG, tag), 104 write_transaction_(trans) { 105 } 106 107 void MutableEntry::PutLocalExternalId(int64 value) { 108 DCHECK(kernel_); 109 write_transaction()->TrackChangesTo(kernel_); 110 if (kernel_->ref(LOCAL_EXTERNAL_ID) != value) { 111 ScopedKernelLock lock(dir()); 112 kernel_->put(LOCAL_EXTERNAL_ID, value); 113 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 114 } 115 } 116 117 void MutableEntry::PutMtime(base::Time value) { 118 DCHECK(kernel_); 119 write_transaction()->TrackChangesTo(kernel_); 120 if (kernel_->ref(MTIME) != value) { 121 kernel_->put(MTIME, value); 122 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 123 } 124 } 125 126 void MutableEntry::PutCtime(base::Time value) { 127 DCHECK(kernel_); 128 write_transaction()->TrackChangesTo(kernel_); 129 if (kernel_->ref(CTIME) != value) { 130 kernel_->put(CTIME, value); 131 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 132 } 133 } 134 135 void MutableEntry::PutParentId(const Id& value) { 136 DCHECK(kernel_); 137 write_transaction()->TrackChangesTo(kernel_); 138 if (kernel_->ref(PARENT_ID) != value) { 139 PutParentIdPropertyOnly(value); 140 if (!GetIsDel()) { 141 if (!PutPredecessor(Id())) { 142 // TODO(lipalani) : Propagate the error to caller. crbug.com/100444. 143 NOTREACHED(); 144 } 145 } 146 } 147 } 148 149 void MutableEntry::PutIsDir(bool value) { 150 DCHECK(kernel_); 151 write_transaction()->TrackChangesTo(kernel_); 152 bool old_value = kernel_->ref(IS_DIR); 153 if (old_value != value) { 154 kernel_->put(IS_DIR, value); 155 kernel_->mark_dirty(GetDirtyIndexHelper()); 156 } 157 } 158 159 void MutableEntry::PutIsDel(bool value) { 160 DCHECK(kernel_); 161 write_transaction()->TrackChangesTo(kernel_); 162 if (value == kernel_->ref(IS_DEL)) { 163 return; 164 } 165 if (value) { 166 // If the server never knew about this item and it's deleted then we don't 167 // need to keep it around. Unsetting IS_UNSYNCED will: 168 // - Ensure that the item is never committed to the server. 169 // - Allow any items with the same UNIQUE_CLIENT_TAG created on other 170 // clients to override this entry. 171 // - Let us delete this entry permanently through 172 // DirectoryBackingStore::DropDeletedEntries() when we next restart sync. 173 // This will save memory and avoid crbug.com/125381. 174 if (!GetId().ServerKnows()) { 175 PutIsUnsynced(false); 176 } 177 } 178 179 { 180 ScopedKernelLock lock(dir()); 181 // Some indices don't include deleted items and must be updated 182 // upon a value change. 183 ScopedParentChildIndexUpdater updater(lock, kernel_, 184 &dir()->kernel_->parent_child_index); 185 186 kernel_->put(IS_DEL, value); 187 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 188 } 189 } 190 191 void MutableEntry::PutNonUniqueName(const std::string& value) { 192 DCHECK(kernel_); 193 write_transaction()->TrackChangesTo(kernel_); 194 195 if (kernel_->ref(NON_UNIQUE_NAME) != value) { 196 kernel_->put(NON_UNIQUE_NAME, value); 197 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 198 } 199 } 200 201 void MutableEntry::PutSpecifics(const sync_pb::EntitySpecifics& value) { 202 DCHECK(kernel_); 203 CHECK(!value.password().has_client_only_encrypted_data()); 204 write_transaction()->TrackChangesTo(kernel_); 205 // TODO(ncarter): This is unfortunately heavyweight. Can we do 206 // better? 207 if (kernel_->ref(SPECIFICS).SerializeAsString() != 208 value.SerializeAsString()) { 209 kernel_->put(SPECIFICS, value); 210 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 211 } 212 } 213 214 void MutableEntry::PutUniquePosition(const UniquePosition& value) { 215 DCHECK(kernel_); 216 write_transaction()->TrackChangesTo(kernel_); 217 if(!kernel_->ref(UNIQUE_POSITION).Equals(value)) { 218 // We should never overwrite a valid position with an invalid one. 219 DCHECK(value.IsValid()); 220 ScopedKernelLock lock(dir()); 221 ScopedParentChildIndexUpdater updater( 222 lock, kernel_, &dir()->kernel_->parent_child_index); 223 kernel_->put(UNIQUE_POSITION, value); 224 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 225 } 226 } 227 228 bool MutableEntry::PutPredecessor(const Id& predecessor_id) { 229 MutableEntry predecessor(write_transaction(), GET_BY_ID, predecessor_id); 230 if (!predecessor.good()) 231 return false; 232 dir()->PutPredecessor(kernel_, predecessor.kernel_); 233 return true; 234 } 235 236 // This function sets only the flags needed to get this entry to sync. 237 bool MarkForSyncing(MutableEntry* e) { 238 DCHECK_NE(static_cast<MutableEntry*>(NULL), e); 239 DCHECK(!e->IsRoot()) << "We shouldn't mark a permanent object for syncing."; 240 if (!(e->PutIsUnsynced(true))) 241 return false; 242 e->PutSyncing(false); 243 return true; 244 } 245 246 } // namespace syncable 247 } // namespace syncer 248