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

Side by Side Diff: components/offline_content/core/offline_content_aggregator.cc

Issue 2690333002: Initial checkin of OfflineContentProvider. (Closed)
Patch Set: ItemsAvailable -> AreItemsAvailable Created 3 years, 10 months 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 2017 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 <algorithm>
6 #include <string>
7
8 #include "base/bind.h"
9 #include "base/memory/ptr_util.h"
10 #include "base/threading/thread_task_runner_handle.h"
11 #include "components/offline_content/core/offline_content_aggregator.h"
12 #include "components/offline_content/core/offline_item.h"
13
14 namespace offline_content {
15
16 namespace {
17
18 // Helper method to determine whether or not |observers| contains |observer|.
19 bool VectorContains(
20 const std::vector<OfflineContentProvider::Observer*>& observers,
21 OfflineContentProvider::Observer* observer) {
22 const auto& it = std::find(observers.cbegin(), observers.cend(), observer);
23 return it != observers.end();
24 }
25
26 // Helper method to remove |observer| from |observers|.
27 void RemoveFromVector(std::vector<OfflineContentProvider::Observer*>& observers,
28 OfflineContentProvider::Observer* observer) {
29 auto it = std::find(observers.begin(), observers.end(), observer);
30 if (it != observers.end())
31 observers.erase(it);
32 }
33
34 } // namespace
35
36 OfflineContentAggregator::OfflineContentAggregator()
37 : sent_on_items_available_(false), weak_ptr_factory_(this) {}
38
39 OfflineContentAggregator::~OfflineContentAggregator() = default;
40
41 void OfflineContentAggregator::RegisterProvider(
42 const std::string& name_space,
43 OfflineContentProvider* provider) {
44 // Validate that this is the first OfflineContentProvider registered that is
45 // associated with |name_space|.
46 DCHECK(providers_.find(name_space) == providers_.end());
47 DCHECK(pending_actions_.find(name_space) == pending_actions_.end());
fgorski 2017/02/14 22:17:55 did you consider adding: DCHECK(provider); ?
David Trainor- moved to gerrit 2017/02/22 01:23:12 Ah good call done!
48
49 providers_[name_space] = provider;
50 provider->AddObserver(this);
51 }
52
53 void OfflineContentAggregator::UnregisterProvider(
54 const std::string& name_space) {
55 auto it = providers_.find(name_space);
56 DCHECK(it != providers_.end());
57
58 it->second->RemoveObserver(this);
59
60 providers_.erase(it);
61 pending_actions_.erase(name_space);
62 }
63
64 bool OfflineContentAggregator::AreItemsAvailable() {
65 return sent_on_items_available_;
66 }
67
68 void OfflineContentAggregator::OpenItem(const ContentId& id) {
69 auto it = providers_.find(id.name_space);
70
71 if (it == providers_.end())
72 return;
73
74 pending_actions_[id.name_space].push_back(base::Bind(
75 &OfflineContentProvider::OpenItem, base::Unretained(it->second), id));
fgorski 2017/02/14 22:17:55 I feel a little uncomfortable about this one. Is t
David Trainor- moved to gerrit 2017/02/22 01:23:12 Ended up checking if the entry was removed from th
76 FlushPendingActionsIfReady(it->second);
fgorski 2017/02/14 22:17:55 Did you consider alternative approach: * run if pr
David Trainor- moved to gerrit 2017/02/22 01:23:11 Yeah that makes sense. Will change.
77 }
78
79 void OfflineContentAggregator::RemoveItem(const ContentId& id) {
80 auto it = providers_.find(id.name_space);
81
82 if (it == providers_.end())
83 return;
84
85 pending_actions_[id.name_space].push_back(base::Bind(
86 &OfflineContentProvider::RemoveItem, base::Unretained(it->second), id));
87 FlushPendingActionsIfReady(it->second);
88 }
89
90 void OfflineContentAggregator::CancelDownload(const ContentId& id) {
91 auto it = providers_.find(id.name_space);
92
93 if (it == providers_.end())
94 return;
95
96 pending_actions_[id.name_space].push_back(
97 base::Bind(&OfflineContentProvider::CancelDownload,
98 base::Unretained(it->second), id));
99 FlushPendingActionsIfReady(it->second);
100 }
101
102 void OfflineContentAggregator::PauseDownload(const ContentId& id) {
103 auto it = providers_.find(id.name_space);
104
105 if (it == providers_.end())
106 return;
107
108 pending_actions_[id.name_space].push_back(
109 base::Bind(&OfflineContentProvider::PauseDownload,
110 base::Unretained(it->second), id));
111 FlushPendingActionsIfReady(it->second);
112 }
113
114 void OfflineContentAggregator::ResumeDownload(const ContentId& id) {
115 auto it = providers_.find(id.name_space);
116
117 if (it == providers_.end())
118 return;
119
120 pending_actions_[id.name_space].push_back(
121 base::Bind(&OfflineContentProvider::ResumeDownload,
122 base::Unretained(it->second), id));
123 FlushPendingActionsIfReady(it->second);
124 }
125
126 OfflineItem* OfflineContentAggregator::GetItemById(const ContentId& id) {
fgorski 2017/02/14 22:17:55 should this be a const OfflineItem*?
David Trainor- moved to gerrit 2017/02/22 01:23:12 Done.
127 auto it = providers_.find(id.name_space);
128
129 if (it == providers_.end() || !it->second->AreItemsAvailable())
130 return nullptr;
131
132 return it->second->GetItemById(id);
133 }
134
135 OfflineContentProvider::OfflineItemList
136 OfflineContentAggregator::GetAllItems() {
137 OfflineItemList items;
138 for (auto& it : providers_) {
139 if (!it.second->AreItemsAvailable())
140 continue;
141
142 OfflineItemList provider_items = it.second->GetAllItems();
143 items.insert(items.end(), provider_items.begin(), provider_items.end());
144 }
145
146 return items;
147 }
148
149 void OfflineContentAggregator::AddObserver(
150 OfflineContentProvider::Observer* observer) {
151 DCHECK(observer);
152 if (observers_.HasObserver(observer))
153 return;
154
155 observers_.AddObserver(observer);
156
157 if (sent_on_items_available_) {
158 base::ThreadTaskRunnerHandle::Get()->PostTask(
159 FROM_HERE,
160 base::Bind(&OfflineContentAggregator::CheckAndNotifyItemsAvailable,
161 weak_ptr_factory_.GetWeakPtr()));
162 }
163 }
164
165 void OfflineContentAggregator::RemoveObserver(
166 OfflineContentProvider::Observer* observer) {
167 DCHECK(observer);
168 if (!observers_.HasObserver(observer))
169 return;
170
171 RemoveFromVector(signaled_observers_, observer);
172 observers_.RemoveObserver(observer);
173 }
174
175 void OfflineContentAggregator::OnItemsAvailable(
176 OfflineContentProvider* provider) {
177 // Flush any pending actions that should be mirrored to the provider.
178 FlushPendingActionsIfReady(provider);
fgorski 2017/02/14 22:17:55 Why did you choose to flush first and then inform
David Trainor- moved to gerrit 2017/02/22 01:23:12 Will document in the header. Let me know if you w
179
180 // Some observers might already be under the impression that this class was
181 // initialized. Just treat this as an OnItemsAdded and notify those observers
182 // of the new items.
183 if (signaled_observers_.size() > 0) {
184 OfflineItemList items = provider->GetAllItems();
185 if (items.size() > 0) {
186 for (auto& observer : signaled_observers_) {
187 observer->OnItemsAdded(items);
188 }
189 }
qinmin 2017/02/14 07:37:20 can we just early return here?
David Trainor- moved to gerrit 2017/02/22 01:23:12 No I don't think so :(. We still need to notify t
190 }
191
192 // Check if there were any observers who haven't been told that this class is
193 // initialized yet. If so, notify them now.
194 CheckAndNotifyItemsAvailable();
195 }
196
197 void OfflineContentAggregator::OnItemsAdded(const OfflineItemList& items) {
198 for (auto& observer : observers_)
199 observer.OnItemsAdded(items);
200 }
201
202 void OfflineContentAggregator::OnItemRemoved(const ContentId& id) {
203 for (auto& observer : observers_)
204 observer.OnItemRemoved(id);
205 }
206
207 void OfflineContentAggregator::OnItemUpdated(const OfflineItem& item) {
208 for (auto& observer : observers_)
209 observer.OnItemUpdated(item);
210 }
211
212 void OfflineContentAggregator::CheckAndNotifyItemsAvailable() {
213 if (providers_.size() == 0)
214 return;
215
216 // If we haven't sent out the initialization message yet, make sure all
217 // underlying OfflineContentProviders are ready before notifying observers
218 // that we're ready to send items.
219 if (!sent_on_items_available_) {
220 for (auto& it : providers_) {
221 if (!it.second->AreItemsAvailable())
222 return;
223 }
224 }
225
226 // Notify all observers who haven't been told about the initialization that we
227 // are initialized. Track the observers so that we don't notify them again.
228 for (auto& observer : observers_) {
229 if (!VectorContains(signaled_observers_, &observer)) {
230 observer.OnItemsAvailable(this);
231 signaled_observers_.push_back(&observer);
232 }
233 }
234
235 // Track that we've told the world that we are initialized.
236 sent_on_items_available_ = true;
237 }
238
239 void OfflineContentAggregator::FlushPendingActionsIfReady(
240 OfflineContentProvider* provider) {
241 if (!provider->AreItemsAvailable())
242 return;
243
244 // Find the actions that correspond to the provider. Requires a reverse
245 // lookup.
246 for (auto& it : providers_) {
247 if (it.second == provider) {
248 // Flush all of the actions to the provider.
249 for (auto& action : pending_actions_[it.first])
250 action.Run();
251 pending_actions_[it.first].clear();
252 return;
253 }
254 }
255 }
256
257 } // namespace offline_content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698