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 DCHECK(missing->empty()); | |
169 success = true; | |
170 break; | |
171 } | |
172 scoped_ptr<ArticleAttachmentsData> attachments_data; | |
173 if (success) { | |
174 attachments_data = ArticleAttachmentsData::GetFromAttachmentMap( | |
175 attachments_proto, *attachments); | |
176 } | |
177 base::MessageLoop::current()->PostTask( | |
178 FROM_HERE, | |
179 base::Bind(callback, success, base::Passed(&attachments_data))); | |
180 } | |
181 | |
65 bool DomDistillerStore::AddEntry(const ArticleEntry& entry) { | 182 bool DomDistillerStore::AddEntry(const ArticleEntry& entry) { |
66 return ChangeEntry(entry, SyncChange::ACTION_ADD); | 183 return ChangeEntry(entry, SyncChange::ACTION_ADD); |
67 } | 184 } |
68 | 185 |
69 bool DomDistillerStore::UpdateEntry(const ArticleEntry& entry) { | 186 bool DomDistillerStore::UpdateEntry(const ArticleEntry& entry) { |
70 return ChangeEntry(entry, SyncChange::ACTION_UPDATE); | 187 return ChangeEntry(entry, SyncChange::ACTION_UPDATE); |
71 } | 188 } |
72 | 189 |
73 bool DomDistillerStore::RemoveEntry(const ArticleEntry& entry) { | 190 bool DomDistillerStore::RemoveEntry(const ArticleEntry& entry) { |
74 return ChangeEntry(entry, SyncChange::ACTION_DELETE); | 191 return ChangeEntry(entry, SyncChange::ACTION_DELETE); |
75 } | 192 } |
76 | 193 |
194 namespace { | |
195 | |
196 bool VerifyAttachmentsUnchanged(const ArticleEntry& entry, | |
197 const DomDistillerModel& model) { | |
198 ArticleEntry currentEntry; | |
199 model.GetEntryById(entry.entry_id(), ¤tEntry); | |
200 DCHECK_EQ(currentEntry.has_attachments(), entry.has_attachments()); | |
201 if (currentEntry.has_attachments()) { | |
202 DCHECK_EQ(currentEntry.attachments().SerializeAsString(), | |
203 entry.attachments().SerializeAsString()); | |
204 } | |
205 return true; | |
206 } | |
207 | |
208 } // namespace | |
209 | |
77 bool DomDistillerStore::ChangeEntry(const ArticleEntry& entry, | 210 bool DomDistillerStore::ChangeEntry(const ArticleEntry& entry, |
78 SyncChange::SyncChangeType changeType) { | 211 SyncChange::SyncChangeType changeType) { |
79 if (!database_loaded_) { | 212 if (!database_loaded_) { |
80 return false; | 213 return false; |
81 } | 214 } |
82 | 215 |
83 bool hasEntry = model_.GetEntryById(entry.entry_id(), NULL); | 216 bool hasEntry = model_.GetEntryById(entry.entry_id(), NULL); |
84 if (hasEntry) { | 217 if (hasEntry) { |
85 if (changeType == SyncChange::ACTION_ADD) { | 218 if (changeType == SyncChange::ACTION_ADD) { |
86 DVLOG(1) << "Already have entry with id " << entry.entry_id() << "."; | 219 DVLOG(1) << "Already have entry with id " << entry.entry_id() << "."; |
87 return false; | 220 return false; |
88 } | 221 } |
222 DCHECK(VerifyAttachmentsUnchanged(entry, model_)); | |
nyquist
2014/11/19 02:26:37
This always returns true, does it really need to b
cjhopman
2014/11/19 19:31:54
The DCHECK ensures that the function isn't called
| |
89 } else if (changeType != SyncChange::ACTION_ADD) { | 223 } else if (changeType != SyncChange::ACTION_ADD) { |
90 DVLOG(1) << "No entry with id " << entry.entry_id() << " found."; | 224 DVLOG(1) << "No entry with id " << entry.entry_id() << " found."; |
91 return false; | 225 return false; |
92 } | 226 } |
93 | 227 |
94 SyncChangeList changes_to_apply; | 228 SyncChangeList changes_to_apply; |
95 changes_to_apply.push_back( | 229 changes_to_apply.push_back( |
96 SyncChange(FROM_HERE, changeType, CreateLocalData(entry))); | 230 SyncChange(FROM_HERE, changeType, CreateLocalData(entry))); |
97 | 231 |
98 SyncChangeList changes_applied; | 232 SyncChangeList changes_applied; |
(...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
333 result.set_num_items_deleted(0); | 467 result.set_num_items_deleted(0); |
334 | 468 |
335 result.set_pre_association_version(0); | 469 result.set_pre_association_version(0); |
336 result.set_num_items_after_association(model_.GetNumEntries()); | 470 result.set_num_items_after_association(model_.GetNumEntries()); |
337 result.set_error(error); | 471 result.set_error(error); |
338 | 472 |
339 return result; | 473 return result; |
340 } | 474 } |
341 | 475 |
342 } // namespace dom_distiller | 476 } // namespace dom_distiller |
OLD | NEW |