Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(24)

Side by Side Diff: ios/chrome/browser/reading_list/reading_list_store.cc

Issue 2451843002: Add Store+Sync to reading list. (Closed)
Patch Set: rebase Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 "ios/chrome/browser/reading_list/reading_list_store.h"
6
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
pavely 2016/11/14 07:45:33 file_path.h is not used.
Olivier 2016/11/14 11:36:49 Done.
9 #include "base/logging.h"
10 #include "base/memory/ptr_util.h"
11 #include "components/sync/model/entity_change.h"
12 #include "components/sync/model/metadata_batch.h"
13 #include "components/sync/model/metadata_change_list.h"
14 #include "components/sync/model/model_type_change_processor.h"
15 #include "components/sync/model/mutable_data_batch.h"
16 #include "components/sync/model_impl/accumulating_metadata_change_list.h"
17 #include "components/sync/protocol/model_type_state.pb.h"
18 #include "ios/chrome/browser/reading_list/proto/reading_list.pb.h"
19 #include "ios/chrome/browser/reading_list/reading_list_model_impl.h"
20 #include "ios/web/public/web_thread.h"
21
22 ReadingListStore::ReadingListStore(
23 StoreFactoryFunction create_store_callback,
24 const ChangeProcessorFactory& change_processor_factory)
25 : ModelTypeSyncBridge(change_processor_factory, syncer::READING_LIST),
26 database_loaded_(false),
27 create_store_callback_(create_store_callback),
28 pending_transaction_(0) {}
29
30 ReadingListStore::~ReadingListStore() {
31 DCHECK(pending_transaction_ == 0);
32 }
33
34 void ReadingListStore::SetReadingListModel(ReadingListModel* model,
35 ReadingListStoreDelegate* delegate) {
36 DCHECK(CalledOnValidThread());
37 model_ = model;
38 delegate_ = delegate;
39 create_store_callback_.Run(
40 base::Bind(&ReadingListStore::OnStoreCreated, base::AsWeakPtr(this)));
41 }
42
43 std::unique_ptr<ReadingListModelStorage::ScopedBatchUpdate>
44 ReadingListStore::EnsureBatchCreated() {
45 return base::WrapUnique<ReadingListModelStorage::ScopedBatchUpdate>(
46 new ScopedBatchUpdate(this));
47 }
48
49 ReadingListStore::ScopedBatchUpdate::ScopedBatchUpdate(ReadingListStore* store)
50 : store_(store) {
51 store_->BeginTransaction();
52 }
53
54 ReadingListStore::ScopedBatchUpdate::~ScopedBatchUpdate() {
55 store_->CommitTransaction();
56 }
57
58 void ReadingListStore::BeginTransaction() {
59 DCHECK(CalledOnValidThread());
60 pending_transaction_++;
61 if (pending_transaction_ == 1) {
62 batch_ = store_->CreateWriteBatch();
63 }
64 }
65
66 void ReadingListStore::CommitTransaction() {
67 DCHECK(CalledOnValidThread());
68 pending_transaction_--;
69 if (pending_transaction_ == 0) {
70 store_->CommitWriteBatch(
71 std::move(batch_),
72 base::Bind(&ReadingListStore::OnDatabaseSave, base::AsWeakPtr(this)));
73 batch_.reset();
74 }
75 }
76
77 void ReadingListStore::SaveEntry(const ReadingListEntry& entry,
78 bool read,
79 bool from_sync) {
80 DCHECK(CalledOnValidThread());
81 auto token = EnsureBatchCreated();
82
83 std::unique_ptr<reading_list::ReadingListLocal> pb_entry =
84 entry.AsReadingListLocal(read);
85
86 batch_->WriteData(entry.URL().spec(), pb_entry->SerializeAsString());
87
88 if (!change_processor()->IsTrackingMetadata() || from_sync) {
89 return;
90 }
91 std::unique_ptr<sync_pb::ReadingListSpecifics> pb_entry_sync =
92 entry.AsReadingListSpecifics(read);
93
94 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
95 CreateMetadataChangeList();
96
97 std::unique_ptr<syncer::EntityData> entity_data(new syncer::EntityData());
98 *entity_data->specifics.mutable_reading_list() = *pb_entry_sync;
99 entity_data->non_unique_name = pb_entry_sync->entry_id();
100
101 change_processor()->Put(entry.URL().spec(), std::move(entity_data),
102 metadata_change_list.get());
103 batch_->TransferMetadataChanges(std::move(metadata_change_list));
104 }
105
106 void ReadingListStore::RemoveEntry(const ReadingListEntry& entry,
107 bool from_sync) {
108 DCHECK(CalledOnValidThread());
109 auto token = EnsureBatchCreated();
110
111 batch_->DeleteData(entry.URL().spec());
112 if (!change_processor()->IsTrackingMetadata() || from_sync) {
113 return;
114 }
115 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
116 CreateMetadataChangeList();
117
118 change_processor()->Delete(entry.URL().spec(), metadata_change_list.get());
119 batch_->TransferMetadataChanges(std::move(metadata_change_list));
120 }
121
122 void ReadingListStore::OnDatabaseLoad(
123 syncer::ModelTypeStore::Result result,
124 std::unique_ptr<syncer::ModelTypeStore::RecordList> entries) {
125 DCHECK(CalledOnValidThread());
126 if (result != syncer::ModelTypeStore::Result::SUCCESS) {
127 return;
pavely 2016/11/14 07:45:34 When initialization fails, bridge should notify ch
Olivier 2016/11/14 11:36:49 Done.
128 }
129 database_loaded_ = true;
130 auto read = base::MakeUnique<ReadingListEntries>();
131 auto unread = base::MakeUnique<ReadingListEntries>();
132
133 for (const syncer::ModelTypeStore::Record& r : *entries.get()) {
134 // for (const reading_list::ReadingListLocal& pb_entry : *entries) {
135 std::unique_ptr<reading_list::ReadingListLocal> proto =
pavely 2016/11/14 07:45:34 You can create proto object on the stack instead o
Olivier 2016/11/14 11:36:49 Done.
136 base::MakeUnique<reading_list::ReadingListLocal>();
137 if (!proto->ParseFromString(r.value)) {
138 continue;
139 // TODO(skym, crbug.com/582460): Handle unrecoverable initialization
140 // failure.
141 }
142
143 std::unique_ptr<ReadingListEntry> entry(
144 ReadingListEntry::FromReadingListLocal(*proto));
145 if (!entry) {
146 continue;
147 }
148 if (proto->status() == reading_list::ReadingListLocal::READ) {
149 read->push_back(std::move(*entry));
150 } else {
151 unread->push_back(std::move(*entry));
152 }
153 }
154
155 delegate_->StoreLoaded(std::move(unread), std::move(read));
156
157 store_->ReadAllMetadata(
158 base::Bind(&ReadingListStore::OnReadAllMetadata, base::AsWeakPtr(this)));
159 }
160
161 void ReadingListStore::OnReadAllMetadata(
162 syncer::SyncError sync_error,
163 std::unique_ptr<syncer::MetadataBatch> metadata_batch) {
164 DCHECK(CalledOnValidThread());
165 change_processor()->OnMetadataLoaded(syncer::SyncError(),
pavely 2016/11/14 07:45:33 You should pass sync_error to OnMetadataLoaded()
Olivier 2016/11/14 11:36:49 Done.
166 std::move(metadata_batch));
167 }
168
169 void ReadingListStore::OnDatabaseSave(syncer::ModelTypeStore::Result result) {
170 return;
171 }
172
173 void ReadingListStore::OnStoreCreated(
174 syncer::ModelTypeStore::Result result,
175 std::unique_ptr<syncer::ModelTypeStore> store) {
176 DCHECK(CalledOnValidThread());
177 store_ = std::move(store);
pavely 2016/11/14 07:45:34 If store creation fails (result != SUCCESS) then s
Olivier 2016/11/14 11:36:49 Done.
178 store_->ReadAllData(
179 base::Bind(&ReadingListStore::OnDatabaseLoad, base::AsWeakPtr(this)));
180 return;
181 }
182
183 syncer::ModelTypeSyncBridge* ReadingListStore::GetModelTypeSyncBridge() {
184 return this;
185 }
186
187 // Creates an object used to communicate changes in the sync metadata to the
188 // model type store.
189 std::unique_ptr<syncer::MetadataChangeList>
190 ReadingListStore::CreateMetadataChangeList() {
191 return syncer::ModelTypeStore::WriteBatch::CreateMetadataChangeList();
192 }
193
194 // Perform the initial merge between local and sync data. This should only be
195 // called when a data type is first enabled to start syncing, and there is no
196 // sync metadata. Best effort should be made to match local and sync data. The
197 // keys in the |entity_data_map| will have been created via GetClientTag(...),
198 // and if a local and sync data should match/merge but disagree on tags, the
199 // service should use the sync data's tag. Any local pieces of data that are
200 // not present in sync should immediately be Put(...) to the processor before
201 // returning. The same MetadataChangeList that was passed into this function
202 // can be passed to Put(...) calls. Delete(...) can also be called but should
203 // not be needed for most model types. Durable storage writes, if not able to
204 // combine all change atomically, should save the metadata after the data
205 // changes, so that this merge will be re-driven by sync if is not completely
206 // saved during the current run.
207 syncer::SyncError ReadingListStore::MergeSyncData(
208 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
209 syncer::EntityDataMap entity_data_map) {
210 DCHECK(CalledOnValidThread());
211 auto token = EnsureBatchCreated();
212 // Keep track of the last update of each item.
213 std::map<GURL, int64_t> last_update;
214 std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate>
215 model_batch_updates = model_->BeginBatchUpdates();
216
217 // Merge sync to local data.
218 for (const auto& kv : entity_data_map) {
219 const sync_pb::ReadingListSpecifics& specifics =
220 kv.second.value().specifics.reading_list();
221 std::unique_ptr<ReadingListEntry> entry(
222 ReadingListEntry::FromReadingListSpecifics(specifics));
223 DCHECK(entry->URL().spec() == kv.first);
224 DCHECK(specifics.entry_id() == kv.first);
225 last_update[entry->URL()] = entry->UpdateTime();
226 // This method will update the model and the local store.
227 delegate_->SyncAddEntry(
228 std::move(entry),
229 specifics.status() == sync_pb::ReadingListSpecifics::READ);
230 }
231
232 int unread_count = model_->unread_size();
233 int read_count = model_->read_size();
234 for (int index = 0; index < unread_count + read_count; index++) {
235 bool read = index >= unread_count;
236 const ReadingListEntry& entry =
237 read ? model_->GetReadEntryAtIndex(index - unread_count)
238 : model_->GetUnreadEntryAtIndex(index);
239 if (last_update.count(entry.URL()) &&
240 last_update[entry.URL()] >= entry.UpdateTime()) {
241 // The synced entry is up to date.
242 continue;
243 }
244 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_pb =
245 entry.AsReadingListSpecifics(read);
246
247 auto entity_data = base::MakeUnique<syncer::EntityData>();
248 *(entity_data->specifics.mutable_reading_list()) = *entry_pb;
249 entity_data->non_unique_name = entry_pb->entry_id();
250
251 change_processor()->Put(entry_pb->entry_id(), std::move(entity_data),
252 metadata_change_list.get());
253 }
254 batch_->TransferMetadataChanges(std::move(metadata_change_list));
255
256 return syncer::SyncError();
257 }
258
259 // Apply changes from the sync server locally.
260 // Please note that |entity_changes| might have fewer entries than
261 // |metadata_change_list| in case when some of the data changes are filtered
262 // out, or even be empty in case when a commit confirmation is processed and
263 // only the metadata needs to persisted.
264 syncer::SyncError ReadingListStore::ApplySyncChanges(
pavely 2016/11/14 07:45:33 Important comment about ApplySyncChanges: metadata
Olivier 2016/11/14 11:36:49 I did two passes and removed the from_sync paramet
265 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
266 syncer::EntityChangeList entity_changes) {
267 DCHECK(CalledOnValidThread());
268 std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate> batch =
269 model_->BeginBatchUpdates();
270 auto token = EnsureBatchCreated();
271
272 for (syncer::EntityChange& change : entity_changes) {
273 if (change.type() == syncer::EntityChange::ACTION_DELETE) {
274 delegate_->SyncRemoveEntry(GURL(change.storage_key()));
275 continue;
276 }
277 const sync_pb::ReadingListSpecifics& specifics =
278 change.data().specifics.reading_list();
279 std::unique_ptr<ReadingListEntry> entry(
280 ReadingListEntry::FromReadingListSpecifics(specifics));
281 delegate_->SyncAddEntry(
282 std::move(entry),
283 specifics.status() == sync_pb::ReadingListSpecifics::READ);
284 }
285 batch_->TransferMetadataChanges(std::move(metadata_change_list));
286
287 return syncer::SyncError();
288 }
289
290 void ReadingListStore::GetData(StorageKeyList storage_keys,
291 DataCallback callback) {
292 DCHECK(CalledOnValidThread());
293 auto batch = base::MakeUnique<syncer::MutableDataBatch>();
294 for (std::string url_string : storage_keys) {
295 bool read;
296 const ReadingListEntry* entry =
297 model_->GetEntryFromURL(GURL(url_string), &read);
298 if (entry) {
299 AddEntryToBatch(batch.get(), *entry, read);
300 }
301 }
302
303 callback.Run(syncer::SyncError(), std::move(batch));
304 }
305
306 void ReadingListStore::GetAllData(DataCallback callback) {
307 DCHECK(CalledOnValidThread());
308 auto batch = base::MakeUnique<syncer::MutableDataBatch>();
309 int unread_count = model_->unread_size();
310 int read_count = model_->read_size();
311 for (int index = 0; index < unread_count + read_count; index++) {
312 bool read = index >= unread_count;
313 const ReadingListEntry& entry =
314 read ? model_->GetReadEntryAtIndex(index - unread_count)
315 : model_->GetUnreadEntryAtIndex(index);
316 AddEntryToBatch(batch.get(), entry, read);
317 }
318
319 callback.Run(syncer::SyncError(), std::move(batch));
320 }
321
322 void ReadingListStore::AddEntryToBatch(syncer::MutableDataBatch* batch,
323 const ReadingListEntry& entry,
324 bool read) {
325 DCHECK(CalledOnValidThread());
326 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_pb =
327 entry.AsReadingListSpecifics(read);
328
329 std::unique_ptr<syncer::EntityData> entity_data(new syncer::EntityData());
330 *(entity_data->specifics.mutable_reading_list()) = *entry_pb;
331 entity_data->non_unique_name = entry_pb->entry_id();
332
333 batch->Put(entry_pb->entry_id(), std::move(entity_data));
334 }
335
336 // Get or generate a client tag for |entity_data|. This must be the same tag
337 // that was/would have been generated in the SyncableService/Directory world
338 // for backward compatibility with pre-USS clients. The only time this
339 // theoretically needs to be called is on the creation of local data, however
340 // it is also used to verify the hash of remote data. If a data type was never
341 // launched pre-USS, then method does not need to be different from
342 // GetStorageKey().
343 std::string ReadingListStore::GetClientTag(
344 const syncer::EntityData& entity_data) {
345 DCHECK(CalledOnValidThread());
346 return GetStorageKey(entity_data);
347 }
348
349 // Get or generate a storage key for |entity_data|. This will only ever be
350 // called once when first encountering a remote entity. Local changes will
351 // provide their storage keys directly to Put instead of using this method.
352 // Theoretically this function doesn't need to be stable across multiple calls
353 // on the same or different clients, but to keep things simple, it probably
354 // should be.
355 std::string ReadingListStore::GetStorageKey(
356 const syncer::EntityData& entity_data) {
357 DCHECK(CalledOnValidThread());
358 return entity_data.specifics.reading_list().entry_id();
359 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698