Home | History | Annotate | Download | only in syncable
      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