| OLD | NEW |
| (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 test whether or not a std::vector contains an item. |
| 19 template <typename T> |
| 20 bool VectorContains(const std::vector<T>& items, T item) { |
| 21 return std::find(items.cbegin(), items.cend(), item) != items.end(); |
| 22 } |
| 23 |
| 24 // Helper method to remove an item from an std::vector. |
| 25 // Helper method to remove |observer| from |observers|. |
| 26 template <typename T> |
| 27 void RemoveFromVector(std::vector<T>& items, T item) { |
| 28 auto it = std::find(items.begin(), items.end(), item); |
| 29 if (it != items.end()) |
| 30 items.erase(it); |
| 31 } |
| 32 |
| 33 } // namespace |
| 34 |
| 35 OfflineContentAggregator::OfflineContentAggregator() |
| 36 : sent_on_items_available_(false), weak_ptr_factory_(this) {} |
| 37 |
| 38 OfflineContentAggregator::~OfflineContentAggregator() = default; |
| 39 |
| 40 void OfflineContentAggregator::RegisterProvider( |
| 41 const std::string& name_space, |
| 42 OfflineContentProvider* provider) { |
| 43 // Validate that this is the first OfflineContentProvider registered that is |
| 44 // associated with |name_space|. |
| 45 DCHECK(provider); |
| 46 DCHECK(providers_.find(name_space) == providers_.end()); |
| 47 DCHECK(pending_actions_.find(provider) == pending_actions_.end()); |
| 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 pending_actions_.erase(it->second); |
| 60 providers_.erase(it); |
| 61 } |
| 62 |
| 63 bool OfflineContentAggregator::AreItemsAvailable() { |
| 64 return sent_on_items_available_; |
| 65 } |
| 66 |
| 67 void OfflineContentAggregator::OpenItem(const ContentId& id) { |
| 68 auto it = providers_.find(id.name_space); |
| 69 |
| 70 if (it == providers_.end()) |
| 71 return; |
| 72 |
| 73 RunIfReady(it->second, base::Bind(&OfflineContentProvider::OpenItem, |
| 74 base::Unretained(it->second), id)); |
| 75 } |
| 76 |
| 77 void OfflineContentAggregator::RemoveItem(const ContentId& id) { |
| 78 auto it = providers_.find(id.name_space); |
| 79 |
| 80 if (it == providers_.end()) |
| 81 return; |
| 82 |
| 83 RunIfReady(it->second, base::Bind(&OfflineContentProvider::RemoveItem, |
| 84 base::Unretained(it->second), id)); |
| 85 } |
| 86 |
| 87 void OfflineContentAggregator::CancelDownload(const ContentId& id) { |
| 88 auto it = providers_.find(id.name_space); |
| 89 |
| 90 if (it == providers_.end()) |
| 91 return; |
| 92 |
| 93 RunIfReady(it->second, base::Bind(&OfflineContentProvider::CancelDownload, |
| 94 base::Unretained(it->second), id)); |
| 95 } |
| 96 |
| 97 void OfflineContentAggregator::PauseDownload(const ContentId& id) { |
| 98 auto it = providers_.find(id.name_space); |
| 99 |
| 100 if (it == providers_.end()) |
| 101 return; |
| 102 |
| 103 RunIfReady(it->second, base::Bind(&OfflineContentProvider::PauseDownload, |
| 104 base::Unretained(it->second), id)); |
| 105 } |
| 106 |
| 107 void OfflineContentAggregator::ResumeDownload(const ContentId& id) { |
| 108 auto it = providers_.find(id.name_space); |
| 109 |
| 110 if (it == providers_.end()) |
| 111 return; |
| 112 |
| 113 RunIfReady(it->second, base::Bind(&OfflineContentProvider::ResumeDownload, |
| 114 base::Unretained(it->second), id)); |
| 115 } |
| 116 |
| 117 const OfflineItem* OfflineContentAggregator::GetItemById(const ContentId& id) { |
| 118 auto it = providers_.find(id.name_space); |
| 119 |
| 120 if (it == providers_.end() || !it->second->AreItemsAvailable()) |
| 121 return nullptr; |
| 122 |
| 123 return it->second->GetItemById(id); |
| 124 } |
| 125 |
| 126 OfflineContentProvider::OfflineItemList |
| 127 OfflineContentAggregator::GetAllItems() { |
| 128 OfflineItemList items; |
| 129 for (auto& it : providers_) { |
| 130 if (!it.second->AreItemsAvailable()) |
| 131 continue; |
| 132 |
| 133 OfflineItemList provider_items = it.second->GetAllItems(); |
| 134 items.insert(items.end(), provider_items.begin(), provider_items.end()); |
| 135 } |
| 136 |
| 137 return items; |
| 138 } |
| 139 |
| 140 void OfflineContentAggregator::AddObserver( |
| 141 OfflineContentProvider::Observer* observer) { |
| 142 DCHECK(observer); |
| 143 if (observers_.HasObserver(observer)) |
| 144 return; |
| 145 |
| 146 observers_.AddObserver(observer); |
| 147 |
| 148 if (sent_on_items_available_) { |
| 149 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 150 FROM_HERE, |
| 151 base::Bind(&OfflineContentAggregator::CheckAndNotifyItemsAvailable, |
| 152 weak_ptr_factory_.GetWeakPtr())); |
| 153 } |
| 154 } |
| 155 |
| 156 void OfflineContentAggregator::RemoveObserver( |
| 157 OfflineContentProvider::Observer* observer) { |
| 158 DCHECK(observer); |
| 159 if (!observers_.HasObserver(observer)) |
| 160 return; |
| 161 |
| 162 RemoveFromVector(signaled_observers_, observer); |
| 163 observers_.RemoveObserver(observer); |
| 164 } |
| 165 |
| 166 void OfflineContentAggregator::OnItemsAvailable( |
| 167 OfflineContentProvider* provider) { |
| 168 // Flush any pending actions that should be mirrored to the provider. |
| 169 FlushPendingActionsIfReady(provider); |
| 170 |
| 171 // Some observers might already be under the impression that this class was |
| 172 // initialized. Just treat this as an OnItemsAdded and notify those observers |
| 173 // of the new items. |
| 174 if (signaled_observers_.size() > 0) { |
| 175 OfflineItemList items = provider->GetAllItems(); |
| 176 if (items.size() > 0) { |
| 177 for (auto& observer : signaled_observers_) { |
| 178 observer->OnItemsAdded(items); |
| 179 } |
| 180 } |
| 181 } |
| 182 |
| 183 // Check if there were any observers who haven't been told that this class is |
| 184 // initialized yet. If so, notify them now. |
| 185 CheckAndNotifyItemsAvailable(); |
| 186 } |
| 187 |
| 188 void OfflineContentAggregator::OnItemsAdded(const OfflineItemList& items) { |
| 189 for (auto& observer : observers_) |
| 190 observer.OnItemsAdded(items); |
| 191 } |
| 192 |
| 193 void OfflineContentAggregator::OnItemRemoved(const ContentId& id) { |
| 194 for (auto& observer : observers_) |
| 195 observer.OnItemRemoved(id); |
| 196 } |
| 197 |
| 198 void OfflineContentAggregator::OnItemUpdated(const OfflineItem& item) { |
| 199 for (auto& observer : observers_) |
| 200 observer.OnItemUpdated(item); |
| 201 } |
| 202 |
| 203 void OfflineContentAggregator::CheckAndNotifyItemsAvailable() { |
| 204 if (providers_.size() == 0) |
| 205 return; |
| 206 |
| 207 // If we haven't sent out the initialization message yet, make sure all |
| 208 // underlying OfflineContentProviders are ready before notifying observers |
| 209 // that we're ready to send items. |
| 210 if (!sent_on_items_available_) { |
| 211 for (auto& it : providers_) { |
| 212 if (!it.second->AreItemsAvailable()) |
| 213 return; |
| 214 } |
| 215 } |
| 216 |
| 217 // Notify all observers who haven't been told about the initialization that we |
| 218 // are initialized. Track the observers so that we don't notify them again. |
| 219 for (auto& observer : observers_) { |
| 220 if (!VectorContains(signaled_observers_, &observer)) { |
| 221 observer.OnItemsAvailable(this); |
| 222 signaled_observers_.push_back(&observer); |
| 223 } |
| 224 } |
| 225 |
| 226 // Track that we've told the world that we are initialized. |
| 227 sent_on_items_available_ = true; |
| 228 } |
| 229 |
| 230 void OfflineContentAggregator::FlushPendingActionsIfReady( |
| 231 OfflineContentProvider* provider) { |
| 232 if (!provider->AreItemsAvailable()) |
| 233 return; |
| 234 |
| 235 CallbackList actions = std::move(pending_actions_[provider]); |
| 236 for (auto& action : actions) { |
| 237 action.Run(); |
| 238 |
| 239 // Check to see if the OfflineContentProvider was removed during the call to |
| 240 // |action|. If so stop the loop. |
| 241 if (pending_actions_.find(provider) == pending_actions_.end()) |
| 242 return; |
| 243 } |
| 244 } |
| 245 |
| 246 void OfflineContentAggregator::RunIfReady(OfflineContentProvider* provider, |
| 247 const base::Closure& action) { |
| 248 if (provider->AreItemsAvailable()) { |
| 249 action.Run(); |
| 250 } else { |
| 251 pending_actions_[provider].push_back(action); |
| 252 } |
| 253 } |
| 254 |
| 255 } // namespace offline_content |
| OLD | NEW |