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, GetTypeRoot, ModelType type)
    102     : ModelNeutralMutableEntry(trans, GET_TYPE_ROOT, type),
    103       write_transaction_(trans) {
    104 }
    105 
    106 void MutableEntry::PutLocalExternalId(int64 value) {
    107   DCHECK(kernel_);
    108   write_transaction()->TrackChangesTo(kernel_);
    109   if (kernel_->ref(LOCAL_EXTERNAL_ID) != value) {
    110     ScopedKernelLock lock(dir());
    111     kernel_->put(LOCAL_EXTERNAL_ID, value);
    112     kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
    113   }
    114 }
    115 
    116 void MutableEntry::PutMtime(base::Time value) {
    117   DCHECK(kernel_);
    118   write_transaction()->TrackChangesTo(kernel_);
    119   if (kernel_->ref(MTIME) != value) {
    120     kernel_->put(MTIME, value);
    121     kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
    122   }
    123 }
    124 
    125 void MutableEntry::PutCtime(base::Time value) {
    126   DCHECK(kernel_);
    127   write_transaction()->TrackChangesTo(kernel_);
    128   if (kernel_->ref(CTIME) != value) {
    129     kernel_->put(CTIME, value);
    130     kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
    131   }
    132 }
    133 
    134 void MutableEntry::PutParentId(const Id& value) {
    135   DCHECK(kernel_);
    136   write_transaction()->TrackChangesTo(kernel_);
    137   if (kernel_->ref(PARENT_ID) != value) {
    138     PutParentIdPropertyOnly(value);
    139     if (!GetIsDel()) {
    140       if (!PutPredecessor(Id())) {
    141         // TODO(lipalani) : Propagate the error to caller. crbug.com/100444.
    142         NOTREACHED();
    143       }
    144     }
    145   }
    146 }
    147 
    148 void MutableEntry::PutIsDir(bool value) {
    149   DCHECK(kernel_);
    150   write_transaction()->TrackChangesTo(kernel_);
    151   bool old_value = kernel_->ref(IS_DIR);
    152   if (old_value != value) {
    153     kernel_->put(IS_DIR, value);
    154     kernel_->mark_dirty(GetDirtyIndexHelper());
    155   }
    156 }
    157 
    158 void MutableEntry::PutIsDel(bool value) {
    159   DCHECK(kernel_);
    160   write_transaction()->TrackChangesTo(kernel_);
    161   if (value == kernel_->ref(IS_DEL)) {
    162     return;
    163   }
    164   if (value) {
    165     // If the server never knew about this item and it's deleted then we don't
    166     // need to keep it around.  Unsetting IS_UNSYNCED will:
    167     // - Ensure that the item is never committed to the server.
    168     // - Allow any items with the same UNIQUE_CLIENT_TAG created on other
    169     //   clients to override this entry.
    170     // - Let us delete this entry permanently through
    171     //   DirectoryBackingStore::DropDeletedEntries() when we next restart sync.
    172     //   This will save memory and avoid crbug.com/125381.
    173     if (!GetId().ServerKnows()) {
    174       PutIsUnsynced(false);
    175     }
    176   }
    177 
    178   {
    179     ScopedKernelLock lock(dir());
    180     // Some indices don't include deleted items and must be updated
    181     // upon a value change.
    182     ScopedParentChildIndexUpdater updater(lock, kernel_,
    183         &dir()->kernel_->parent_child_index);
    184 
    185     kernel_->put(IS_DEL, value);
    186     kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
    187   }
    188 }
    189 
    190 void MutableEntry::PutNonUniqueName(const std::string& value) {
    191   DCHECK(kernel_);
    192   write_transaction()->TrackChangesTo(kernel_);
    193 
    194   if (kernel_->ref(NON_UNIQUE_NAME) != value) {
    195     kernel_->put(NON_UNIQUE_NAME, value);
    196     kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
    197   }
    198 }
    199 
    200 void MutableEntry::PutSpecifics(const sync_pb::EntitySpecifics& value) {
    201   DCHECK(kernel_);
    202   CHECK(!value.password().has_client_only_encrypted_data());
    203   write_transaction()->TrackChangesTo(kernel_);
    204   // TODO(ncarter): This is unfortunately heavyweight.  Can we do
    205   // better?
    206   if (kernel_->ref(SPECIFICS).SerializeAsString() !=
    207       value.SerializeAsString()) {
    208     kernel_->put(SPECIFICS, value);
    209     kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
    210   }
    211 }
    212 
    213 void MutableEntry::PutUniquePosition(const UniquePosition& value) {
    214   DCHECK(kernel_);
    215   write_transaction()->TrackChangesTo(kernel_);
    216   if(!kernel_->ref(UNIQUE_POSITION).Equals(value)) {
    217     // We should never overwrite a valid position with an invalid one.
    218     DCHECK(value.IsValid());
    219     ScopedKernelLock lock(dir());
    220     ScopedParentChildIndexUpdater updater(
    221         lock, kernel_, &dir()->kernel_->parent_child_index);
    222     kernel_->put(UNIQUE_POSITION, value);
    223     kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
    224   }
    225 }
    226 
    227 bool MutableEntry::PutPredecessor(const Id& predecessor_id) {
    228   MutableEntry predecessor(write_transaction(), GET_BY_ID, predecessor_id);
    229   if (!predecessor.good())
    230     return false;
    231   dir()->PutPredecessor(kernel_, predecessor.kernel_);
    232   return true;
    233 }
    234 
    235 void MutableEntry::PutAttachmentMetadata(
    236     const sync_pb::AttachmentMetadata& attachment_metadata) {
    237   DCHECK(kernel_);
    238   write_transaction()->TrackChangesTo(kernel_);
    239   if (kernel_->ref(ATTACHMENT_METADATA).SerializeAsString() !=
    240       attachment_metadata.SerializeAsString()) {
    241     dir()->UpdateAttachmentIndex(GetMetahandle(),
    242                                  kernel_->ref(ATTACHMENT_METADATA),
    243                                  attachment_metadata);
    244     kernel_->put(ATTACHMENT_METADATA, attachment_metadata);
    245     kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
    246   }
    247 }
    248 
    249 void MutableEntry::MarkAttachmentAsOnServer(
    250     const sync_pb::AttachmentIdProto& attachment_id) {
    251   DCHECK(kernel_);
    252   DCHECK(!attachment_id.unique_id().empty());
    253   write_transaction()->TrackChangesTo(kernel_);
    254   sync_pb::AttachmentMetadata& attachment_metadata =
    255       kernel_->mutable_ref(ATTACHMENT_METADATA);
    256   for (int i = 0; i < attachment_metadata.record_size(); ++i) {
    257     sync_pb::AttachmentMetadataRecord* record =
    258         attachment_metadata.mutable_record(i);
    259     if (record->id().unique_id() != attachment_id.unique_id())
    260       continue;
    261     record->set_is_on_server(true);
    262   }
    263   kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
    264   MarkForSyncing(this);
    265 }
    266 
    267 // This function sets only the flags needed to get this entry to sync.
    268 bool MarkForSyncing(MutableEntry* e) {
    269   DCHECK_NE(static_cast<MutableEntry*>(NULL), e);
    270   DCHECK(!e->IsRoot()) << "We shouldn't mark a permanent object for syncing.";
    271   if (!(e->PutIsUnsynced(true)))
    272     return false;
    273   e->PutSyncing(false);
    274   return true;
    275 }
    276 
    277 }  // namespace syncable
    278 }  // namespace syncer
    279