Index: sync/syncable/syncable.cc |
diff --git a/sync/syncable/syncable.cc b/sync/syncable/syncable.cc |
index 760a3314a40bef6e12d9aa191cde77925235322e..68b5babe10da50c9fdd83e1e0636166d83d47b42 100644 |
--- a/sync/syncable/syncable.cc |
+++ b/sync/syncable/syncable.cc |
@@ -1736,6 +1736,18 @@ bool MutableEntry::PutIsDel(bool is_del) { |
if (!UnlinkFromOrder()) { |
return false; |
} |
+ |
+ // If the server never knew about this item and it's deleted then we don't |
+ // need to keep it around. Unsetting IS_UNSYNCED will: |
+ // - Ensure that the item is never committed to the server. |
+ // - Allow any items with the same UNIQUE_CLIENT_TAG created on other |
+ // clients to override this entry. |
+ // - Let us delete this entry permanently through |
+ // DirectoryBackingStore::DropDeletedEntries() when we next restart sync. |
+ // This will save memory and avoid crbug.com/125381. |
+ if (!Get(ID).ServerKnows()) { |
+ Put(IS_UNSYNCED, false); |
+ } |
} |
{ |
@@ -2376,4 +2388,47 @@ EntryKernel* Directory::GetPossibleLastChildForTest( |
return NULL; |
} |
+void ChangeEntryIDAndUpdateChildren( |
+ syncable::WriteTransaction* trans, |
+ syncable::MutableEntry* entry, |
+ const syncable::Id& new_id) { |
+ syncable::Id old_id = entry->Get(ID); |
+ if (!entry->Put(ID, new_id)) { |
+ Entry old_entry(trans, GET_BY_ID, new_id); |
+ CHECK(old_entry.good()); |
+ LOG(FATAL) << "Attempt to change ID to " << new_id |
+ << " conflicts with existing entry.\n\n" |
+ << *entry << "\n\n" << old_entry; |
+ } |
+ if (entry->Get(IS_DIR)) { |
+ // Get all child entries of the old id. |
+ syncable::Directory::ChildHandles children; |
+ trans->directory()->GetChildHandlesById(trans, old_id, &children); |
+ Directory::ChildHandles::iterator i = children.begin(); |
+ while (i != children.end()) { |
+ MutableEntry child_entry(trans, GET_BY_HANDLE, *i++); |
+ CHECK(child_entry.good()); |
+ // Use the unchecked setter here to avoid touching the child's NEXT_ID |
+ // and PREV_ID fields (which Put(PARENT_ID) would normally do to |
+ // maintain linked-list invariants). In this case, NEXT_ID and PREV_ID |
+ // among the children will be valid after the loop, since we update all |
+ // the children at once. |
+ child_entry.PutParentIdPropertyOnly(new_id); |
+ } |
+ } |
+ // Update Id references on the previous and next nodes in the sibling |
+ // order. Do this by reinserting into the linked list; the first |
+ // step in PutPredecessor is to Unlink from the existing order, which |
+ // will overwrite the stale Id value from the adjacent nodes. |
+ if (entry->Get(PREV_ID) == entry->Get(NEXT_ID) && |
+ entry->Get(PREV_ID) == old_id) { |
+ // We just need a shallow update to |entry|'s fields since it is already |
+ // self looped. |
+ entry->Put(NEXT_ID, new_id); |
+ entry->Put(PREV_ID, new_id); |
+ } else { |
+ entry->PutPredecessor(entry->Get(PREV_ID)); |
+ } |
+} |
+ |
} // namespace syncable |