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

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

Issue 2514333003: Componentize 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/logging.h"
9 #include "base/memory/ptr_util.h"
10 #include "components/sync/model/entity_change.h"
11 #include "components/sync/model/metadata_batch.h"
12 #include "components/sync/model/metadata_change_list.h"
13 #include "components/sync/model/model_type_change_processor.h"
14 #include "components/sync/model/mutable_data_batch.h"
15 #include "components/sync/model_impl/accumulating_metadata_change_list.h"
16 #include "components/sync/protocol/model_type_state.pb.h"
17 #include "ios/chrome/browser/reading_list/proto/reading_list.pb.h"
18 #include "ios/chrome/browser/reading_list/reading_list_model_impl.h"
19 #include "ios/web/public/web_thread.h"
20
21 ReadingListStore::ReadingListStore(
22 StoreFactoryFunction create_store_callback,
23 const ChangeProcessorFactory& change_processor_factory)
24 : ModelTypeSyncBridge(change_processor_factory, syncer::READING_LIST),
25 create_store_callback_(create_store_callback),
26 pending_transaction_count_(0) {}
27
28 ReadingListStore::~ReadingListStore() {
29 DCHECK(pending_transaction_count_ == 0);
30 }
31
32 void ReadingListStore::SetReadingListModel(ReadingListModel* model,
33 ReadingListStoreDelegate* delegate) {
34 DCHECK(CalledOnValidThread());
35 model_ = model;
36 delegate_ = delegate;
37 create_store_callback_.Run(
38 base::Bind(&ReadingListStore::OnStoreCreated, base::AsWeakPtr(this)));
39 }
40
41 std::unique_ptr<ReadingListModelStorage::ScopedBatchUpdate>
42 ReadingListStore::EnsureBatchCreated() {
43 return base::WrapUnique<ReadingListModelStorage::ScopedBatchUpdate>(
44 new ScopedBatchUpdate(this));
45 }
46
47 ReadingListStore::ScopedBatchUpdate::ScopedBatchUpdate(ReadingListStore* store)
48 : store_(store) {
49 store_->BeginTransaction();
50 }
51
52 ReadingListStore::ScopedBatchUpdate::~ScopedBatchUpdate() {
53 store_->CommitTransaction();
54 }
55
56 void ReadingListStore::BeginTransaction() {
57 DCHECK(CalledOnValidThread());
58 pending_transaction_count_++;
59 if (pending_transaction_count_ == 1) {
60 batch_ = store_->CreateWriteBatch();
61 }
62 }
63
64 void ReadingListStore::CommitTransaction() {
65 DCHECK(CalledOnValidThread());
66 pending_transaction_count_--;
67 if (pending_transaction_count_ == 0) {
68 store_->CommitWriteBatch(
69 std::move(batch_),
70 base::Bind(&ReadingListStore::OnDatabaseSave, base::AsWeakPtr(this)));
71 batch_.reset();
72 }
73 }
74
75 void ReadingListStore::SaveEntry(const ReadingListEntry& entry, bool read) {
76 DCHECK(CalledOnValidThread());
77 auto token = EnsureBatchCreated();
78
79 std::unique_ptr<reading_list::ReadingListLocal> pb_entry =
80 entry.AsReadingListLocal(read);
81
82 batch_->WriteData(entry.URL().spec(), pb_entry->SerializeAsString());
83
84 if (!change_processor()->IsTrackingMetadata()) {
85 return;
86 }
87 std::unique_ptr<sync_pb::ReadingListSpecifics> pb_entry_sync =
88 entry.AsReadingListSpecifics(read);
89
90 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
91 CreateMetadataChangeList();
92
93 std::unique_ptr<syncer::EntityData> entity_data(new syncer::EntityData());
94 *entity_data->specifics.mutable_reading_list() = *pb_entry_sync;
95 entity_data->non_unique_name = pb_entry_sync->entry_id();
96
97 change_processor()->Put(entry.URL().spec(), std::move(entity_data),
98 metadata_change_list.get());
99 batch_->TransferMetadataChanges(std::move(metadata_change_list));
100 }
101
102 void ReadingListStore::RemoveEntry(const ReadingListEntry& entry) {
103 DCHECK(CalledOnValidThread());
104 auto token = EnsureBatchCreated();
105
106 batch_->DeleteData(entry.URL().spec());
107 if (!change_processor()->IsTrackingMetadata()) {
108 return;
109 }
110 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
111 CreateMetadataChangeList();
112
113 change_processor()->Delete(entry.URL().spec(), metadata_change_list.get());
114 batch_->TransferMetadataChanges(std::move(metadata_change_list));
115 }
116
117 void ReadingListStore::OnDatabaseLoad(
118 syncer::ModelTypeStore::Result result,
119 std::unique_ptr<syncer::ModelTypeStore::RecordList> entries) {
120 DCHECK(CalledOnValidThread());
121 if (result != syncer::ModelTypeStore::Result::SUCCESS) {
122 change_processor()->OnMetadataLoaded(
123 change_processor()->CreateAndUploadError(
124 FROM_HERE, "Cannot load Reading List Database."),
125 nullptr);
126 return;
127 }
128 auto read = base::MakeUnique<ReadingListEntries>();
129 auto unread = base::MakeUnique<ReadingListEntries>();
130
131 for (const syncer::ModelTypeStore::Record& r : *entries.get()) {
132 // for (const reading_list::ReadingListLocal& pb_entry : *entries) {
133 reading_list::ReadingListLocal proto;
134 if (!proto.ParseFromString(r.value)) {
135 continue;
136 // TODO(skym, crbug.com/582460): Handle unrecoverable initialization
137 // failure.
138 }
139
140 std::unique_ptr<ReadingListEntry> entry(
141 ReadingListEntry::FromReadingListLocal(proto));
142 if (!entry) {
143 continue;
144 }
145 if (proto.status() == reading_list::ReadingListLocal::READ) {
146 read->push_back(std::move(*entry));
147 } else {
148 unread->push_back(std::move(*entry));
149 }
150 }
151
152 delegate_->StoreLoaded(std::move(unread), std::move(read));
153
154 store_->ReadAllMetadata(
155 base::Bind(&ReadingListStore::OnReadAllMetadata, base::AsWeakPtr(this)));
156 }
157
158 void ReadingListStore::OnReadAllMetadata(
159 syncer::SyncError sync_error,
160 std::unique_ptr<syncer::MetadataBatch> metadata_batch) {
161 DCHECK(CalledOnValidThread());
162 change_processor()->OnMetadataLoaded(sync_error, std::move(metadata_batch));
163 }
164
165 void ReadingListStore::OnDatabaseSave(syncer::ModelTypeStore::Result result) {
166 return;
167 }
168
169 void ReadingListStore::OnStoreCreated(
170 syncer::ModelTypeStore::Result result,
171 std::unique_ptr<syncer::ModelTypeStore> store) {
172 DCHECK(CalledOnValidThread());
173 if (result != syncer::ModelTypeStore::Result::SUCCESS) {
174 // TODO(crbug.com/664926): handle store creation error.
175 return;
176 }
177 store_ = std::move(store);
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::set<std::string> synced_entries;
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 synced_entries.insert(kv.first);
220 const sync_pb::ReadingListSpecifics& specifics =
221 kv.second.value().specifics.reading_list();
222 // Deserialize entry.
223 bool read = specifics.status() == sync_pb::ReadingListSpecifics::READ;
224 std::unique_ptr<ReadingListEntry> entry(
225 ReadingListEntry::FromReadingListSpecifics(specifics));
226
227 bool was_read;
228 const ReadingListEntry* existing_entry =
229 model_->GetEntryFromURL(entry->URL(), &was_read);
230
231 if (!existing_entry) {
232 // This entry is new. Add it to the store and model.
233 // Convert to local store format and write to store.
234 std::unique_ptr<reading_list::ReadingListLocal> entry_pb =
235 entry->AsReadingListLocal(read);
236 batch_->WriteData(entry->URL().spec(), entry_pb->SerializeAsString());
237
238 // Notify model about updated entry.
239 delegate_->SyncAddEntry(std::move(entry), read);
240 } else if (existing_entry->UpdateTime() < entry->UpdateTime()) {
241 // The entry from sync is more recent.
242 // Merge the local data to it and store it.
243 ReadingListEntry* merged_entry =
244 delegate_->SyncMergeEntry(std::move(entry), read);
245
246 // Write to the store.
247 std::unique_ptr<reading_list::ReadingListLocal> entry_local_pb =
248 merged_entry->AsReadingListLocal(read);
249 batch_->WriteData(entry->URL().spec(),
250 entry_local_pb->SerializeAsString());
251
252 // Send to sync
253 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_sync_pb =
254 merged_entry->AsReadingListSpecifics(read);
255 auto entity_data = base::MakeUnique<syncer::EntityData>();
256 *(entity_data->specifics.mutable_reading_list()) = *entry_sync_pb;
257 entity_data->non_unique_name = entry_sync_pb->entry_id();
258
259 // TODO(crbug.com/666232): Investigate if there is a risk of sync
260 // ping-pong.
261 change_processor()->Put(entry_sync_pb->entry_id(), std::move(entity_data),
262 metadata_change_list.get());
263
264 } else {
265 // The entry from sync is out of date.
266 // Send back the local more recent entry.
267 // No need to update
268 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_pb =
269 existing_entry->AsReadingListSpecifics(was_read);
270 auto entity_data = base::MakeUnique<syncer::EntityData>();
271 *(entity_data->specifics.mutable_reading_list()) = *entry_pb;
272 entity_data->non_unique_name = entry_pb->entry_id();
273
274 change_processor()->Put(entry_pb->entry_id(), std::move(entity_data),
275 metadata_change_list.get());
276 }
277 }
278
279 // Commit local only entries to server.
280 int unread_count = model_->unread_size();
281 int read_count = model_->read_size();
282 for (int index = 0; index < unread_count + read_count; index++) {
283 bool read = index >= unread_count;
284 const ReadingListEntry& entry =
285 read ? model_->GetReadEntryAtIndex(index - unread_count)
286 : model_->GetUnreadEntryAtIndex(index);
287 if (synced_entries.count(entry.URL().spec())) {
288 // Entry already exists and has been merged above.
289 continue;
290 }
291
292 // Local entry has later timestamp. It should be committed to server.
293 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_pb =
294 entry.AsReadingListSpecifics(read);
295
296 auto entity_data = base::MakeUnique<syncer::EntityData>();
297 *(entity_data->specifics.mutable_reading_list()) = *entry_pb;
298 entity_data->non_unique_name = entry_pb->entry_id();
299
300 change_processor()->Put(entry_pb->entry_id(), std::move(entity_data),
301 metadata_change_list.get());
302 }
303 batch_->TransferMetadataChanges(std::move(metadata_change_list));
304
305 return syncer::SyncError();
306 }
307
308 // Apply changes from the sync server locally.
309 // Please note that |entity_changes| might have fewer entries than
310 // |metadata_change_list| in case when some of the data changes are filtered
311 // out, or even be empty in case when a commit confirmation is processed and
312 // only the metadata needs to persisted.
313 syncer::SyncError ReadingListStore::ApplySyncChanges(
314 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
315 syncer::EntityChangeList entity_changes) {
316 DCHECK(CalledOnValidThread());
317 std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate> batch =
318 model_->BeginBatchUpdates();
319 auto token = EnsureBatchCreated();
320
321 for (syncer::EntityChange& change : entity_changes) {
322 if (change.type() == syncer::EntityChange::ACTION_DELETE) {
323 batch_->DeleteData(change.storage_key());
324 // Need to notify model that entry is deleted.
325 delegate_->SyncRemoveEntry(GURL(change.storage_key()));
326 } else {
327 // Deserialize entry.
328 const sync_pb::ReadingListSpecifics& specifics =
329 change.data().specifics.reading_list();
330 bool read = specifics.status() == sync_pb::ReadingListSpecifics::READ;
331 std::unique_ptr<ReadingListEntry> entry(
332 ReadingListEntry::FromReadingListSpecifics(specifics));
333
334 bool was_read;
335 const ReadingListEntry* existing_entry =
336 model_->GetEntryFromURL(entry->URL(), &was_read);
337
338 if (!existing_entry) {
339 // This entry is new. Add it to the store and model.
340 // Convert to local store format and write to store.
341 std::unique_ptr<reading_list::ReadingListLocal> entry_pb =
342 entry->AsReadingListLocal(read);
343 batch_->WriteData(entry->URL().spec(), entry_pb->SerializeAsString());
344
345 // Notify model about updated entry.
346 delegate_->SyncAddEntry(std::move(entry), read);
347 } else if (existing_entry->UpdateTime() < entry->UpdateTime()) {
348 // The entry from sync is more recent.
349 // Merge the local data to it and store it.
350 ReadingListEntry* merged_entry =
351 delegate_->SyncMergeEntry(std::move(entry), read);
352
353 // Write to the store.
354 std::unique_ptr<reading_list::ReadingListLocal> entry_local_pb =
355 merged_entry->AsReadingListLocal(read);
356 batch_->WriteData(merged_entry->URL().spec(),
357 entry_local_pb->SerializeAsString());
358
359 // Send to sync
360 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_sync_pb =
361 merged_entry->AsReadingListSpecifics(read);
362 auto entity_data = base::MakeUnique<syncer::EntityData>();
363 *(entity_data->specifics.mutable_reading_list()) = *entry_sync_pb;
364 entity_data->non_unique_name = entry_sync_pb->entry_id();
365
366 // TODO(crbug.com/666232): Investigate if there is a risk of sync
367 // ping-pong.
368 change_processor()->Put(entry_sync_pb->entry_id(),
369 std::move(entity_data),
370 metadata_change_list.get());
371
372 } else {
373 // The entry from sync is out of date.
374 // Send back the local more recent entry.
375 // No need to update
376 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_pb =
377 existing_entry->AsReadingListSpecifics(was_read);
378 auto entity_data = base::MakeUnique<syncer::EntityData>();
379 *(entity_data->specifics.mutable_reading_list()) = *entry_pb;
380 entity_data->non_unique_name = entry_pb->entry_id();
381
382 change_processor()->Put(entry_pb->entry_id(), std::move(entity_data),
383 metadata_change_list.get());
384 }
385 }
386 }
387
388 batch_->TransferMetadataChanges(std::move(metadata_change_list));
389 return syncer::SyncError();
390 }
391
392 void ReadingListStore::GetData(StorageKeyList storage_keys,
393 DataCallback callback) {
394 DCHECK(CalledOnValidThread());
395 auto batch = base::MakeUnique<syncer::MutableDataBatch>();
396 for (std::string url_string : storage_keys) {
397 bool read;
398 const ReadingListEntry* entry =
399 model_->GetEntryFromURL(GURL(url_string), &read);
400 if (entry) {
401 AddEntryToBatch(batch.get(), *entry, read);
402 }
403 }
404
405 callback.Run(syncer::SyncError(), std::move(batch));
406 }
407
408 void ReadingListStore::GetAllData(DataCallback callback) {
409 DCHECK(CalledOnValidThread());
410 auto batch = base::MakeUnique<syncer::MutableDataBatch>();
411 int unread_count = model_->unread_size();
412 int read_count = model_->read_size();
413 for (int index = 0; index < unread_count + read_count; index++) {
414 bool read = index >= unread_count;
415 const ReadingListEntry& entry =
416 read ? model_->GetReadEntryAtIndex(index - unread_count)
417 : model_->GetUnreadEntryAtIndex(index);
418 AddEntryToBatch(batch.get(), entry, read);
419 }
420
421 callback.Run(syncer::SyncError(), std::move(batch));
422 }
423
424 void ReadingListStore::AddEntryToBatch(syncer::MutableDataBatch* batch,
425 const ReadingListEntry& entry,
426 bool read) {
427 DCHECK(CalledOnValidThread());
428 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_pb =
429 entry.AsReadingListSpecifics(read);
430
431 std::unique_ptr<syncer::EntityData> entity_data(new syncer::EntityData());
432 *(entity_data->specifics.mutable_reading_list()) = *entry_pb;
433 entity_data->non_unique_name = entry_pb->entry_id();
434
435 batch->Put(entry_pb->entry_id(), std::move(entity_data));
436 }
437
438 // Get or generate a client tag for |entity_data|. This must be the same tag
439 // that was/would have been generated in the SyncableService/Directory world
440 // for backward compatibility with pre-USS clients. The only time this
441 // theoretically needs to be called is on the creation of local data, however
442 // it is also used to verify the hash of remote data. If a data type was never
443 // launched pre-USS, then method does not need to be different from
444 // GetStorageKey().
445 std::string ReadingListStore::GetClientTag(
446 const syncer::EntityData& entity_data) {
447 DCHECK(CalledOnValidThread());
448 return GetStorageKey(entity_data);
449 }
450
451 // Get or generate a storage key for |entity_data|. This will only ever be
452 // called once when first encountering a remote entity. Local changes will
453 // provide their storage keys directly to Put instead of using this method.
454 // Theoretically this function doesn't need to be stable across multiple calls
455 // on the same or different clients, but to keep things simple, it probably
456 // should be.
457 std::string ReadingListStore::GetStorageKey(
458 const syncer::EntityData& entity_data) {
459 DCHECK(CalledOnValidThread());
460 return entity_data.specifics.reading_list().entry_id();
461 }
OLDNEW
« no previous file with comments | « ios/chrome/browser/reading_list/reading_list_store.h ('k') | ios/chrome/browser/reading_list/reading_list_store_delegate.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698