OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/dom_distiller/core/dom_distiller_store.h" | 5 #include "components/dom_distiller/core/dom_distiller_store.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/message_loop/message_loop.h" | |
9 #include "components/dom_distiller/core/article_entry.h" | 10 #include "components/dom_distiller/core/article_entry.h" |
10 #include "sync/api/sync_change.h" | 11 #include "sync/api/sync_change.h" |
11 #include "sync/protocol/article_specifics.pb.h" | 12 #include "sync/protocol/article_specifics.pb.h" |
12 #include "sync/protocol/sync.pb.h" | 13 #include "sync/protocol/sync.pb.h" |
13 | 14 |
14 using leveldb_proto::ProtoDatabase; | 15 using leveldb_proto::ProtoDatabase; |
15 using sync_pb::ArticleSpecifics; | 16 using sync_pb::ArticleSpecifics; |
16 using sync_pb::EntitySpecifics; | 17 using sync_pb::EntitySpecifics; |
17 using syncer::ModelType; | 18 using syncer::ModelType; |
18 using syncer::SyncChange; | 19 using syncer::SyncChange; |
19 using syncer::SyncChangeList; | 20 using syncer::SyncChangeList; |
20 using syncer::SyncData; | 21 using syncer::SyncData; |
21 using syncer::SyncDataList; | 22 using syncer::SyncDataList; |
22 using syncer::SyncError; | 23 using syncer::SyncError; |
23 using syncer::SyncMergeResult; | 24 using syncer::SyncMergeResult; |
24 | 25 |
25 namespace dom_distiller { | 26 namespace dom_distiller { |
26 | 27 |
27 DomDistillerStore::DomDistillerStore( | 28 DomDistillerStore::DomDistillerStore( |
28 scoped_ptr<ProtoDatabase<ArticleEntry> > database, | 29 scoped_ptr<ProtoDatabase<ArticleEntry> > database, |
29 const base::FilePath& database_dir) | 30 const base::FilePath& database_dir) |
30 : database_(database.Pass()), | 31 : database_(database.Pass()), |
31 database_loaded_(false), | 32 database_loaded_(false), |
33 attachment_store_(syncer::AttachmentStore::CreateInMemoryStore()), | |
32 weak_ptr_factory_(this) { | 34 weak_ptr_factory_(this) { |
33 database_->Init(database_dir, base::Bind(&DomDistillerStore::OnDatabaseInit, | 35 database_->Init(database_dir, base::Bind(&DomDistillerStore::OnDatabaseInit, |
34 weak_ptr_factory_.GetWeakPtr())); | 36 weak_ptr_factory_.GetWeakPtr())); |
35 } | 37 } |
36 | 38 |
37 DomDistillerStore::DomDistillerStore( | 39 DomDistillerStore::DomDistillerStore( |
38 scoped_ptr<ProtoDatabase<ArticleEntry> > database, | 40 scoped_ptr<ProtoDatabase<ArticleEntry> > database, |
39 const std::vector<ArticleEntry>& initial_data, | 41 const std::vector<ArticleEntry>& initial_data, |
40 const base::FilePath& database_dir) | 42 const base::FilePath& database_dir) |
41 : database_(database.Pass()), | 43 : database_(database.Pass()), |
42 database_loaded_(false), | 44 database_loaded_(false), |
45 attachment_store_(syncer::AttachmentStore::CreateInMemoryStore()), | |
43 model_(initial_data), | 46 model_(initial_data), |
44 weak_ptr_factory_(this) { | 47 weak_ptr_factory_(this) { |
45 database_->Init(database_dir, base::Bind(&DomDistillerStore::OnDatabaseInit, | 48 database_->Init(database_dir, base::Bind(&DomDistillerStore::OnDatabaseInit, |
46 weak_ptr_factory_.GetWeakPtr())); | 49 weak_ptr_factory_.GetWeakPtr())); |
47 } | 50 } |
48 | 51 |
49 DomDistillerStore::~DomDistillerStore() {} | 52 DomDistillerStore::~DomDistillerStore() {} |
50 | 53 |
51 // DomDistillerStoreInterface implementation. | 54 // DomDistillerStoreInterface implementation. |
52 syncer::SyncableService* DomDistillerStore::GetSyncableService() { | 55 syncer::SyncableService* DomDistillerStore::GetSyncableService() { |
53 return this; | 56 return this; |
54 } | 57 } |
55 | 58 |
56 bool DomDistillerStore::GetEntryById(const std::string& entry_id, | 59 bool DomDistillerStore::GetEntryById(const std::string& entry_id, |
57 ArticleEntry* entry) { | 60 ArticleEntry* entry) { |
58 return model_.GetEntryById(entry_id, entry); | 61 return model_.GetEntryById(entry_id, entry); |
59 } | 62 } |
60 | 63 |
61 bool DomDistillerStore::GetEntryByUrl(const GURL& url, ArticleEntry* entry) { | 64 bool DomDistillerStore::GetEntryByUrl(const GURL& url, ArticleEntry* entry) { |
62 return model_.GetEntryByUrl(url, entry); | 65 return model_.GetEntryByUrl(url, entry); |
63 } | 66 } |
64 | 67 |
68 void DomDistillerStore::UpdateAttachments( | |
69 const std::string& entry_id, | |
70 scoped_ptr<ArticleAttachmentsData> attachments_data, | |
71 const UpdateAttachmentsCallback& callback) { | |
72 if (!GetEntryById(entry_id, nullptr)) { | |
73 base::MessageLoop::current()->PostTask(FROM_HERE, | |
74 base::Bind(callback, false)); | |
75 } | |
76 | |
77 scoped_ptr<sync_pb::ArticleAttachments> article_attachments( | |
78 new sync_pb::ArticleAttachments()); | |
79 syncer::AttachmentList attachment_list; | |
80 attachments_data->CreateSyncAttachments(&attachment_list, | |
81 article_attachments.get()); | |
82 | |
83 attachment_store_->Write( | |
84 attachment_list, | |
85 base::Bind(&DomDistillerStore::OnAttachmentsWrite, | |
86 weak_ptr_factory_.GetWeakPtr(), entry_id, | |
87 base::Passed(&article_attachments), callback)); | |
88 } | |
89 | |
90 void DomDistillerStore::OnAttachmentsWrite( | |
91 const std::string& entry_id, | |
92 scoped_ptr<sync_pb::ArticleAttachments> article_attachments, | |
93 const UpdateAttachmentsCallback& callback, | |
94 const syncer::AttachmentStore::Result& result) { | |
95 bool success = false; | |
96 switch (result) { | |
97 case syncer::AttachmentStore::UNSPECIFIED_ERROR: | |
98 break; | |
99 case syncer::AttachmentStore::SUCCESS: | |
100 success = true; | |
101 break; | |
102 } | |
103 | |
104 if (success) { | |
105 ArticleEntry entry; | |
106 bool has_entry = GetEntryById(entry_id, &entry); | |
107 if (!has_entry) { | |
108 success = false; | |
109 attachment_store_->Drop(GetAttachmentIds(*article_attachments), | |
110 syncer::AttachmentStore::DropCallback()); | |
111 } else { | |
112 if (entry.has_attachments()) { | |
113 attachment_store_->Drop(GetAttachmentIds(entry.attachments()), | |
114 syncer::AttachmentStore::DropCallback()); | |
115 } | |
116 entry.set_allocated_attachments(article_attachments.release()); | |
117 | |
118 SyncChangeList changes_to_apply; | |
119 changes_to_apply.push_back(SyncChange( | |
120 FROM_HERE, SyncChange::ACTION_UPDATE, CreateLocalData(entry))); | |
121 | |
122 SyncChangeList changes_applied; | |
123 SyncChangeList changes_missing; | |
124 | |
125 ApplyChangesToModel(changes_to_apply, &changes_applied, &changes_missing); | |
126 | |
127 DCHECK_EQ(size_t(0), changes_missing.size()); | |
128 DCHECK_EQ(size_t(1), changes_applied.size()); | |
129 | |
130 ApplyChangesToSync(FROM_HERE, changes_applied); | |
131 ApplyChangesToDatabase(changes_applied); | |
132 } | |
133 } | |
134 base::MessageLoop::current()->PostTask(FROM_HERE, | |
135 base::Bind(callback, success)); | |
136 } | |
137 | |
138 void DomDistillerStore::GetAttachments( | |
139 const std::string& entry_id, | |
140 const GetAttachmentsCallback& callback) { | |
141 ArticleEntry entry; | |
142 if (!model_.GetEntryById(entry_id, &entry) | |
143 || !entry.has_attachments()) { | |
144 base::MessageLoop::current()->PostTask( | |
145 FROM_HERE, base::Bind(callback, false, nullptr)); | |
146 return; | |
147 } | |
148 | |
149 // TODO(cjhopman): This should use GetOrDownloadAttachments() once there is a | |
150 // feasible way to use that. | |
151 attachment_store_->Read(GetAttachmentIds(entry.attachments()), | |
152 base::Bind(&DomDistillerStore::OnAttachmentsRead, | |
153 weak_ptr_factory_.GetWeakPtr(), | |
154 entry.attachments(), callback)); | |
155 } | |
156 | |
157 void DomDistillerStore::OnAttachmentsRead( | |
158 const sync_pb::ArticleAttachments& attachments_proto, | |
159 const GetAttachmentsCallback& callback, | |
160 const syncer::AttachmentStore::Result& result, | |
161 scoped_ptr<syncer::AttachmentMap> attachments, | |
162 scoped_ptr<syncer::AttachmentIdList> missing) { | |
163 bool success = false; | |
164 switch (result) { | |
165 case syncer::AttachmentStore::UNSPECIFIED_ERROR: | |
166 break; | |
167 case syncer::AttachmentStore::SUCCESS: | |
168 success = missing->empty(); | |
pavely
2014/11/17 19:09:00
Minor comment: if result is SUCCESS then |missing|
cjhopman
2014/11/18 20:57:45
Done.
| |
169 break; | |
170 } | |
171 scoped_ptr<ArticleAttachmentsData> attachments_data; | |
172 if (success) { | |
173 attachments_data = ArticleAttachmentsData::GetFromAttachmentMap( | |
174 attachments_proto, *attachments); | |
175 } | |
176 base::MessageLoop::current()->PostTask( | |
177 FROM_HERE, | |
178 base::Bind(callback, success, base::Passed(&attachments_data))); | |
179 } | |
180 | |
65 bool DomDistillerStore::AddEntry(const ArticleEntry& entry) { | 181 bool DomDistillerStore::AddEntry(const ArticleEntry& entry) { |
66 return ChangeEntry(entry, SyncChange::ACTION_ADD); | 182 return ChangeEntry(entry, SyncChange::ACTION_ADD); |
67 } | 183 } |
68 | 184 |
69 bool DomDistillerStore::UpdateEntry(const ArticleEntry& entry) { | 185 bool DomDistillerStore::UpdateEntry(const ArticleEntry& entry) { |
70 return ChangeEntry(entry, SyncChange::ACTION_UPDATE); | 186 return ChangeEntry(entry, SyncChange::ACTION_UPDATE); |
71 } | 187 } |
72 | 188 |
73 bool DomDistillerStore::RemoveEntry(const ArticleEntry& entry) { | 189 bool DomDistillerStore::RemoveEntry(const ArticleEntry& entry) { |
74 return ChangeEntry(entry, SyncChange::ACTION_DELETE); | 190 return ChangeEntry(entry, SyncChange::ACTION_DELETE); |
75 } | 191 } |
76 | 192 |
193 namespace { | |
194 | |
195 bool VerifyAttachmentsUnchanged(const ArticleEntry& entry, | |
196 const DomDistillerModel& model) { | |
197 ArticleEntry currentEntry; | |
198 model.GetEntryById(entry.entry_id(), ¤tEntry); | |
199 DCHECK_EQ(currentEntry.has_attachments(), entry.has_attachments()); | |
200 if (currentEntry.has_attachments()) { | |
201 DCHECK_EQ(currentEntry.attachments().SerializeAsString(), | |
202 entry.attachments().SerializeAsString()); | |
203 } | |
204 return true; | |
205 } | |
206 | |
207 } // namespace | |
208 | |
77 bool DomDistillerStore::ChangeEntry(const ArticleEntry& entry, | 209 bool DomDistillerStore::ChangeEntry(const ArticleEntry& entry, |
78 SyncChange::SyncChangeType changeType) { | 210 SyncChange::SyncChangeType changeType) { |
79 if (!database_loaded_) { | 211 if (!database_loaded_) { |
80 return false; | 212 return false; |
81 } | 213 } |
82 | 214 |
83 bool hasEntry = model_.GetEntryById(entry.entry_id(), NULL); | 215 bool hasEntry = model_.GetEntryById(entry.entry_id(), NULL); |
84 if (hasEntry) { | 216 if (hasEntry) { |
85 if (changeType == SyncChange::ACTION_ADD) { | 217 if (changeType == SyncChange::ACTION_ADD) { |
86 DVLOG(1) << "Already have entry with id " << entry.entry_id() << "."; | 218 DVLOG(1) << "Already have entry with id " << entry.entry_id() << "."; |
87 return false; | 219 return false; |
88 } | 220 } |
221 DCHECK(VerifyAttachmentsUnchanged(entry, model_)); | |
89 } else if (changeType != SyncChange::ACTION_ADD) { | 222 } else if (changeType != SyncChange::ACTION_ADD) { |
90 DVLOG(1) << "No entry with id " << entry.entry_id() << " found."; | 223 DVLOG(1) << "No entry with id " << entry.entry_id() << " found."; |
91 return false; | 224 return false; |
92 } | 225 } |
93 | 226 |
94 SyncChangeList changes_to_apply; | 227 SyncChangeList changes_to_apply; |
95 changes_to_apply.push_back( | 228 changes_to_apply.push_back( |
96 SyncChange(FROM_HERE, changeType, CreateLocalData(entry))); | 229 SyncChange(FROM_HERE, changeType, CreateLocalData(entry))); |
97 | 230 |
98 SyncChangeList changes_applied; | 231 SyncChangeList changes_applied; |
(...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
333 result.set_num_items_deleted(0); | 466 result.set_num_items_deleted(0); |
334 | 467 |
335 result.set_pre_association_version(0); | 468 result.set_pre_association_version(0); |
336 result.set_num_items_after_association(model_.GetNumEntries()); | 469 result.set_num_items_after_association(model_.GetNumEntries()); |
337 result.set_error(error); | 470 result.set_error(error); |
338 | 471 |
339 return result; | 472 return result; |
340 } | 473 } |
341 | 474 |
342 } // namespace dom_distiller | 475 } // namespace dom_distiller |
OLD | NEW |