Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 "chrome/browser/ntp_snippets/downloads/download_suggestions_provider.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/guid.h" | |
| 11 #include "base/strings/string_number_conversions.h" | |
| 12 #include "base/strings/string_util.h" | |
| 13 #include "base/strings/utf_string_conversions.h" | |
| 14 #include "base/threading/thread_task_runner_handle.h" | |
| 15 #include "base/time/time.h" | |
| 16 #include "components/ntp_snippets/pref_names.h" | |
| 17 #include "components/ntp_snippets/pref_util.h" | |
| 18 #include "components/offline_pages/client_namespace_constants.h" | |
| 19 #include "components/prefs/pref_registry_simple.h" | |
| 20 #include "components/prefs/pref_service.h" | |
| 21 #include "grit/components_strings.h" | |
| 22 #include "net/base/filename_util.h" | |
| 23 #include "ui/base/l10n/l10n_util.h" | |
| 24 #include "ui/gfx/image/image.h" | |
| 25 | |
| 26 using content::DownloadItem; | |
| 27 using content::DownloadManager; | |
| 28 using offline_pages::OfflinePageItem; | |
| 29 | |
| 30 namespace ntp_snippets { | |
| 31 | |
| 32 namespace { | |
| 33 | |
| 34 const int kMaxSuggestionsCount = 5; | |
|
Marc Treib
2016/10/11 12:07:40
This should probably be configurable via a variati
vitaliii
2016/10/13 08:43:06
Done.
| |
| 35 const char kAssetDownloadsPrefix[] = "D"; | |
| 36 const char kOfflinePageDownloadsPrefix[] = "O"; | |
|
Marc Treib
2016/10/11 12:07:40
Just make these two chars (not array)
vitaliii
2016/10/13 08:43:06
Done.
| |
| 37 | |
| 38 std::string GetOfflinePagePerCategoryID(int64_t raw_offline_page_id) { | |
| 39 // Raw ID is prefixed in order to avoid conflicts with asset downloads. | |
| 40 return base::JoinString( | |
| 41 {kOfflinePageDownloadsPrefix, base::IntToString(raw_offline_page_id)}, | |
| 42 ""); | |
|
Marc Treib
2016/10/11 12:07:41
s/""/std::string()/
But if you're just appending
vitaliii
2016/10/13 08:43:05
Done.
| |
| 43 } | |
| 44 | |
| 45 std::string GetAssetDownloadPerCategoryID(uint32_t raw_download_id) { | |
| 46 // Raw ID is prefixed in order to avoid conflicts with offline page downloads. | |
| 47 return base::JoinString( | |
| 48 {kAssetDownloadsPrefix, base::UintToString(raw_download_id)}, ""); | |
|
Marc Treib
2016/10/11 12:07:41
Same here
vitaliii
2016/10/13 08:43:05
Done.
| |
| 49 } | |
| 50 | |
| 51 // Determines whether |suggestion_id| corresponds to offline page suggestion or | |
| 52 // asset download based on |id_within_category| prefix. | |
| 53 bool IsOfflinePageID(const ContentSuggestion::ID& suggestion_id) { | |
|
Marc Treib
2016/10/11 12:07:40
nit: "OfflinePageID" is somewhat overloaded. Maybe
vitaliii
2016/10/13 08:43:05
Done.
| |
| 54 const std::string& id_within_category = suggestion_id.id_within_category(); | |
| 55 if (!id_within_category.empty()) { | |
| 56 if (id_within_category[0] == kOfflinePageDownloadsPrefix[0]) | |
| 57 return true; | |
| 58 if (id_within_category[0] == kAssetDownloadsPrefix[0]) | |
| 59 return false; | |
| 60 } | |
| 61 NOTREACHED() << "Unknown id_within_category " << id_within_category; | |
| 62 return false; | |
| 63 } | |
| 64 | |
| 65 struct OrderOfflinePagesByMostRecentlyVisitedFirst { | |
| 66 bool operator()(const OfflinePageItem* left, | |
| 67 const OfflinePageItem* right) const { | |
| 68 return left->last_access_time > right->last_access_time; | |
| 69 } | |
| 70 }; | |
| 71 | |
| 72 bool IsOfflinePageDownload(const offline_pages::ClientId& client_id) { | |
| 73 return (client_id.name_space == offline_pages::kAsyncNamespace || | |
| 74 client_id.name_space == offline_pages::kDownloadNamespace || | |
| 75 client_id.name_space == offline_pages::kNTPSuggestionsNamespace) && | |
|
Marc Treib
2016/10/11 12:07:41
What's kNTPSuggestionsNamespace?
vitaliii
2016/10/13 08:43:05
This is for offlined NTP items, e.g. right click o
| |
| 76 base::IsValidGUID(client_id.id); | |
|
Marc Treib
2016/10/11 12:07:41
Is this needed? Would we ever expect to get an inv
vitaliii
2016/10/13 08:43:06
It was done like that previously (and the logic wa
| |
| 77 } | |
| 78 | |
| 79 bool IsDownloadCompleted(const DownloadItem* item) { | |
|
Marc Treib
2016/10/11 12:07:40
nit: "const DownloadItem&" if it can't be null
vitaliii
2016/10/13 08:43:06
Wow, I did not know that this is possible with poi
| |
| 80 return item->GetState() == DownloadItem::DownloadState::COMPLETE && | |
| 81 !item->GetFileExternallyRemoved(); | |
| 82 } | |
| 83 | |
| 84 struct OrderDownloadsMostRecentlyDownloadedCompletedFirst { | |
| 85 bool operator()(const DownloadItem* left, const DownloadItem* right) const { | |
| 86 if (IsDownloadCompleted(left) != IsDownloadCompleted(right)) | |
| 87 return IsDownloadCompleted(left); | |
| 88 return left->GetEndTime() > right->GetEndTime(); | |
| 89 } | |
| 90 }; | |
| 91 | |
| 92 struct SuggestionItemWrapper { | |
| 93 base::Time time; | |
| 94 bool is_offline_page; | |
| 95 int index; | |
| 96 bool operator<(const SuggestionItemWrapper& other) const { | |
| 97 return time > other.time; | |
| 98 } | |
| 99 }; | |
| 100 | |
| 101 } // namespace | |
| 102 | |
| 103 DownloadSuggestionsProvider::DownloadSuggestionsProvider( | |
| 104 ContentSuggestionsProvider::Observer* observer, | |
| 105 CategoryFactory* category_factory, | |
| 106 const scoped_refptr<OfflinePageProxy>& offline_page_proxy, | |
| 107 content::DownloadManager* download_manager, | |
| 108 PrefService* pref_service, | |
| 109 bool download_manager_ui_enabled) | |
| 110 : ContentSuggestionsProvider(observer, category_factory), | |
| 111 category_status_(CategoryStatus::AVAILABLE_LOADING), | |
| 112 provided_category_( | |
| 113 category_factory->FromKnownCategory(KnownCategories::DOWNLOADS)), | |
| 114 offline_page_proxy_(offline_page_proxy), | |
| 115 download_manager_notifier_(download_manager, this), | |
| 116 pref_service_(pref_service), | |
| 117 download_manager_ui_enabled_(download_manager_ui_enabled), | |
| 118 weak_ptr_factory_(this) { | |
| 119 observer->OnCategoryStatusChanged(this, provided_category_, category_status_); | |
| 120 offline_page_proxy_->AddObserver(this); | |
| 121 FetchAllDownloadsAndSubmitSuggestions(); | |
| 122 } | |
| 123 | |
| 124 DownloadSuggestionsProvider::~DownloadSuggestionsProvider() { | |
| 125 offline_page_proxy_->RemoveObserver(this); | |
| 126 } | |
| 127 | |
| 128 CategoryStatus DownloadSuggestionsProvider::GetCategoryStatus( | |
| 129 Category category) { | |
| 130 if (category == provided_category_) | |
| 131 return category_status_; | |
| 132 NOTREACHED() << "Unknown category " << category.id(); | |
| 133 return CategoryStatus::NOT_PROVIDED; | |
|
Marc Treib
2016/10/11 12:07:40
Just do
DCHECK_EQ(provided_category_, category);
r
vitaliii
2016/10/13 08:43:05
Done.
| |
| 134 } | |
| 135 | |
| 136 CategoryInfo DownloadSuggestionsProvider::GetCategoryInfo(Category category) { | |
| 137 if (category == provided_category_) { | |
| 138 return CategoryInfo( | |
| 139 l10n_util::GetStringUTF16(IDS_NTP_DOWNLOAD_SUGGESTIONS_SECTION_HEADER), | |
| 140 ContentSuggestionsCardLayout::MINIMAL_CARD, | |
| 141 /*has_more_button=*/download_manager_ui_enabled_, | |
| 142 /*show_if_empty=*/false); | |
| 143 } | |
| 144 NOTREACHED() << "Unknown category " << category.id(); | |
|
Marc Treib
2016/10/11 12:07:40
Same here
vitaliii
2016/10/13 08:43:06
Done.
| |
| 145 return CategoryInfo(base::string16(), | |
| 146 ContentSuggestionsCardLayout::MINIMAL_CARD, | |
| 147 /*has_more_button=*/false, | |
| 148 /*show_if_empty=*/false); | |
| 149 } | |
| 150 | |
| 151 void DownloadSuggestionsProvider::DismissSuggestion( | |
| 152 const ContentSuggestion::ID& suggestion_id) { | |
| 153 DCHECK_EQ(provided_category_, suggestion_id.category()); | |
| 154 std::set<std::string> dismissed_ids = | |
| 155 ReadDismissedIDsFromPrefs(suggestion_id); | |
| 156 dismissed_ids.insert(suggestion_id.id_within_category()); | |
| 157 StoreDismissedIDsToPrefs(suggestion_id, dismissed_ids); | |
|
Marc Treib
2016/10/11 12:07:41
nit: I find it a bit confusing to pass in the sugg
vitaliii
2016/10/13 08:43:05
I did the former.
The latter is less attractive,
| |
| 158 | |
| 159 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded(suggestion_id); | |
| 160 } | |
| 161 | |
| 162 void DownloadSuggestionsProvider::FetchSuggestionImage( | |
| 163 const ContentSuggestion::ID& suggestion_id, | |
| 164 const ImageFetchedCallback& callback) { | |
| 165 // TODO(vitaliii): Fetch proper thumbnail from OfflinePageModel once it is | |
| 166 // available there. | |
| 167 // TODO(vitaliii): Provide site's favicon for assets downloads. | |
| 168 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 169 FROM_HERE, base::Bind(callback, gfx::Image())); | |
| 170 } | |
| 171 | |
| 172 void DownloadSuggestionsProvider::ClearHistory( | |
| 173 base::Time begin, | |
| 174 base::Time end, | |
| 175 const base::Callback<bool(const GURL& url)>& filter) { | |
| 176 ClearDismissedSuggestionsForDebugging(provided_category_); | |
|
Marc Treib
2016/10/11 12:07:40
This will call FetchAllDownloadsAndSubmitSuggestio
vitaliii
2016/10/13 08:43:05
Good point! Actually current cache is completely i
Marc Treib
2016/10/13 12:11:25
Hm, but that is async, right? So maybe it's best t
vitaliii
2016/10/15 18:36:30
Done.
| |
| 177 cached_offline_page_downloads_.clear(); | |
| 178 cached_asset_downloads_.clear(); | |
| 179 FetchAllDownloadsAndSubmitSuggestions(); | |
| 180 } | |
| 181 | |
| 182 void DownloadSuggestionsProvider::ClearCachedSuggestions(Category category) { | |
| 183 DCHECK_EQ(provided_category_, category); | |
| 184 cached_offline_page_downloads_.clear(); | |
| 185 cached_asset_downloads_.clear(); | |
| 186 FetchAllDownloadsAndSubmitSuggestions(); | |
|
Marc Treib
2016/10/11 12:07:41
Would we expect this to change anything? The cache
vitaliii
2016/10/13 08:43:05
Good point.
| |
| 187 } | |
| 188 | |
| 189 void DownloadSuggestionsProvider::GetDismissedSuggestionsForDebugging( | |
| 190 Category category, | |
| 191 const DismissedSuggestionsCallback& callback) { | |
| 192 DCHECK_EQ(provided_category_, category); | |
| 193 // TODO(vitaliii): Implement. | |
| 194 callback.Run(std::vector<ContentSuggestion>()); | |
| 195 } | |
| 196 | |
| 197 void DownloadSuggestionsProvider::ClearDismissedSuggestionsForDebugging( | |
| 198 Category category) { | |
| 199 DCHECK_EQ(provided_category_, category); | |
| 200 StoreAssetDismissedIDsToPrefs(std::set<std::string>()); | |
| 201 StoreOfflinePageDismissedIDsToPrefs(std::set<std::string>()); | |
| 202 FetchAllDownloadsAndSubmitSuggestions(); | |
| 203 } | |
| 204 | |
| 205 // static | |
| 206 void DownloadSuggestionsProvider::RegisterProfilePrefs( | |
| 207 PrefRegistrySimple* registry) { | |
| 208 registry->RegisterListPref(prefs::kDismissedAssetDownloadSuggestions); | |
| 209 registry->RegisterListPref(prefs::kDismissedOfflinePageDownloadSuggestions); | |
| 210 } | |
| 211 | |
| 212 //////////////////////////////////////////////////////////////////////////////// | |
| 213 // Private methods | |
| 214 | |
| 215 void DownloadSuggestionsProvider::OfflinePageModelChanged( | |
| 216 const std::vector<offline_pages::OfflinePageItem>& offline_pages) { | |
| 217 NotifyStatusChanged(CategoryStatus::AVAILABLE); | |
|
Marc Treib
2016/10/11 12:07:41
SubmitContentSuggestions will change the status, s
vitaliii
2016/10/13 08:43:07
Yes, but it will happen just before the new sugges
Marc Treib
2016/10/13 12:11:25
But all this happens synchronously, so the UI does
vitaliii
2016/10/15 18:36:30
Done.
| |
| 218 ProcessAllOfflinePages(offline_pages); | |
| 219 SubmitContentSuggestions(); | |
| 220 } | |
| 221 | |
| 222 void DownloadSuggestionsProvider::OfflinePageDeleted( | |
| 223 int64_t offline_id, | |
| 224 const offline_pages::ClientId& client_id) { | |
| 225 // Because we never switch to NOT_PROVIDED dynamically, there can be no open | |
| 226 // UI containing an invalidated suggestion unless the status is something | |
| 227 // other than NOT_PROVIDED, so only notify invalidation in that case. | |
|
Marc Treib
2016/10/11 12:07:41
Actually, can the status *ever* be NOT_PROVIDED no
vitaliii
2016/10/13 08:43:05
Looks like not, but I am not sure.
However, I do
| |
| 228 if (category_status_ != CategoryStatus::NOT_PROVIDED && | |
| 229 IsOfflinePageDownload(client_id)) { | |
| 230 InvalidateSuggestion(GetOfflinePagePerCategoryID(offline_id)); | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 void DownloadSuggestionsProvider::OnDownloadCreated(DownloadManager* manager, | |
| 235 DownloadItem* item) { | |
| 236 // This is called when new downloads are started and on startup for existing | |
| 237 // ones. | |
| 238 if (CacheAssetDownloadIfNeeded(item)) | |
| 239 SubmitContentSuggestions(); | |
| 240 } | |
| 241 | |
| 242 void DownloadSuggestionsProvider::OnDownloadUpdated(DownloadManager* manager, | |
| 243 DownloadItem* item) { | |
| 244 // Unfinished downloads may become completed, therefore, this call cannot be | |
| 245 // ignored. | |
| 246 if (CacheAssetDownloadIfNeeded(item)) | |
| 247 SubmitContentSuggestions(); | |
| 248 } | |
| 249 void DownloadSuggestionsProvider::OnDownloadOpened(DownloadManager* manager, | |
|
Marc Treib
2016/10/11 12:07:40
nit: empty line before
vitaliii
2016/10/13 08:43:06
Done.
| |
| 250 DownloadItem* item) { | |
| 251 // Ignored. | |
| 252 } | |
| 253 void DownloadSuggestionsProvider::OnDownloadRemoved(DownloadManager* manager, | |
|
Marc Treib
2016/10/11 12:07:40
Also here
vitaliii
2016/10/13 08:43:06
Done.
| |
| 254 DownloadItem* item) { | |
| 255 if (!IsDownloadCompleted(item)) | |
| 256 return; | |
| 257 // TODO(vitaliii): Implement a better way to clean up dismissed IDs (in case | |
| 258 // some calls are missed). | |
| 259 InvalidateSuggestion(GetAssetDownloadPerCategoryID(item->GetId())); | |
| 260 } | |
| 261 | |
| 262 void DownloadSuggestionsProvider::NotifyStatusChanged( | |
| 263 CategoryStatus new_status) { | |
| 264 DCHECK_NE(CategoryStatus::NOT_PROVIDED, category_status_); | |
|
Marc Treib
2016/10/11 12:07:40
Should this check new_status instead of category_s
vitaliii
2016/10/13 08:43:07
Done.
| |
| 265 if (category_status_ == new_status) | |
| 266 return; | |
| 267 category_status_ = new_status; | |
| 268 observer()->OnCategoryStatusChanged(this, provided_category_, new_status); | |
|
Marc Treib
2016/10/11 12:07:40
nit: s/new_status/category_status_/
vitaliii
2016/10/13 08:43:06
Done.
| |
| 269 } | |
| 270 | |
| 271 void DownloadSuggestionsProvider::FetchOfflinePagesDownloads() { | |
| 272 offline_page_proxy_->GetAllPages( | |
| 273 base::Bind(&DownloadSuggestionsProvider::ProcessAllOfflinePages, | |
| 274 weak_ptr_factory_.GetWeakPtr())); | |
| 275 } | |
| 276 | |
| 277 void DownloadSuggestionsProvider:: | |
| 278 FetchOfflinePagesDownloadsAndSubmitSuggestions() { | |
| 279 offline_page_proxy_->GetAllPages( | |
| 280 base::Bind(&DownloadSuggestionsProvider::OfflinePageModelChanged, | |
| 281 weak_ptr_factory_.GetWeakPtr())); | |
| 282 } | |
| 283 | |
| 284 void DownloadSuggestionsProvider::FetchAssetsDownloads() { | |
| 285 DownloadManager* manager = download_manager_notifier_.GetManager(); | |
| 286 if (!manager) { | |
| 287 // The manager has gone down. | |
| 288 return; | |
| 289 } | |
| 290 | |
| 291 NotifyStatusChanged(CategoryStatus::AVAILABLE); | |
|
Marc Treib
2016/10/11 12:07:41
Why is this here?
vitaliii
2016/10/13 08:43:07
Done.
| |
| 292 | |
| 293 std::vector<DownloadItem*> downloads; | |
| 294 manager->GetAllDownloads(&downloads); | |
| 295 | |
| 296 std::set<std::string> dismissed_ids = ReadAssetDismissedIDsFromPrefs(); | |
| 297 std::set<std::string> new_dismissed_ids; | |
|
Marc Treib
2016/10/11 12:07:40
Maybe old_dismissed_ids and retained_dismissed_ids
vitaliii
2016/10/13 08:43:06
Done.
| |
| 298 std::vector<DownloadItem*> not_dismissed_downloads; | |
| 299 for (DownloadItem* item : downloads) { | |
|
Marc Treib
2016/10/11 12:07:41
const?
vitaliii
2016/10/13 08:43:05
Done.
| |
| 300 std::string within_category_id = | |
| 301 GetAssetDownloadPerCategoryID(item->GetId()); | |
| 302 if (!dismissed_ids.count(within_category_id)) { | |
| 303 not_dismissed_downloads.push_back(item); | |
| 304 } else { | |
| 305 new_dismissed_ids.insert(within_category_id); | |
| 306 } | |
| 307 } | |
| 308 | |
| 309 if (dismissed_ids.size() != new_dismissed_ids.size()) | |
| 310 StoreAssetDismissedIDsToPrefs(new_dismissed_ids); | |
| 311 | |
| 312 downloads = not_dismissed_downloads; | |
|
Marc Treib
2016/10/11 12:07:40
Maybe rename "downloads" to "all_downloads", "not_
vitaliii
2016/10/13 08:43:06
Done.
| |
| 313 if (static_cast<int>(downloads.size()) > kMaxSuggestionsCount) { | |
| 314 std::nth_element(downloads.begin(), | |
|
Marc Treib
2016/10/11 12:07:41
Nice!
But since nth_element isn't very common, and
vitaliii
2016/10/13 08:43:06
Done.
| |
| 315 downloads.begin() + kMaxSuggestionsCount, downloads.end(), | |
| 316 OrderDownloadsMostRecentlyDownloadedCompletedFirst()); | |
| 317 downloads.resize(kMaxSuggestionsCount); | |
| 318 } | |
| 319 | |
| 320 cached_asset_downloads_.clear(); | |
| 321 for (DownloadItem* item : downloads) { | |
| 322 if (IsDownloadCompleted(item)) | |
|
Marc Treib
2016/10/11 12:07:41
Wouldn't it be easier if the filtering happened be
vitaliii
2016/10/13 08:43:06
Done.
| |
| 323 cached_asset_downloads_.push_back(item); | |
| 324 } | |
| 325 } | |
| 326 | |
| 327 void DownloadSuggestionsProvider::FetchAllDownloadsAndSubmitSuggestions() { | |
| 328 FetchAssetsDownloads(); | |
| 329 FetchOfflinePagesDownloadsAndSubmitSuggestions(); | |
|
Marc Treib
2016/10/11 12:07:41
I was gonna say that this isn't safe since the off
vitaliii
2016/10/13 08:43:05
Done.
| |
| 330 } | |
| 331 | |
| 332 void DownloadSuggestionsProvider::SubmitContentSuggestions() { | |
| 333 NotifyStatusChanged(CategoryStatus::AVAILABLE); | |
| 334 | |
| 335 std::vector<SuggestionItemWrapper> suggestion_items; | |
| 336 for (int i = 0; i < static_cast<int>(cached_offline_page_downloads_.size()); | |
| 337 ++i) { | |
| 338 SuggestionItemWrapper wrapped_item; | |
| 339 wrapped_item.is_offline_page = true; | |
| 340 wrapped_item.index = i; | |
|
Marc Treib
2016/10/11 12:07:41
Mh, this is a bit yucky. Maybe instead of bool+ind
vitaliii
2016/10/13 08:43:05
Acknowledged.
Marc Treib
2016/10/13 12:11:25
...this reply confused me ;P
"Obsolete, Suggestion
vitaliii
2016/10/15 18:36:29
I've got it. Sorry about that.
| |
| 341 wrapped_item.time = cached_offline_page_downloads_[i].last_access_time; | |
| 342 suggestion_items.push_back(wrapped_item); | |
| 343 } | |
| 344 | |
| 345 for (int i = 0; i < static_cast<int>(cached_asset_downloads_.size()); ++i) { | |
| 346 SuggestionItemWrapper wrapped_item; | |
| 347 wrapped_item.is_offline_page = false; | |
| 348 wrapped_item.index = i; | |
| 349 wrapped_item.time = cached_asset_downloads_[i]->GetEndTime(); | |
| 350 suggestion_items.push_back(wrapped_item); | |
| 351 } | |
| 352 | |
| 353 std::sort(suggestion_items.begin(), suggestion_items.end()); | |
|
Marc Treib
2016/10/11 12:07:40
Or in fact: Can the sorting just happen after conv
vitaliii
2016/10/13 08:43:06
Done.
| |
| 354 | |
| 355 std::vector<ContentSuggestion> suggestions; | |
| 356 for (const SuggestionItemWrapper& wrapped_item : suggestion_items) { | |
| 357 if (suggestions.size() >= kMaxSuggestionsCount) | |
| 358 break; | |
| 359 | |
| 360 if (wrapped_item.is_offline_page) { | |
| 361 suggestions.push_back(ConvertOfflinePage( | |
| 362 cached_offline_page_downloads_[wrapped_item.index])); | |
| 363 } else { | |
| 364 suggestions.push_back( | |
| 365 ConvertDownloadItem(cached_asset_downloads_[wrapped_item.index])); | |
| 366 } | |
| 367 } | |
| 368 | |
| 369 observer()->OnNewSuggestions(this, provided_category_, | |
| 370 std::move(suggestions)); | |
| 371 } | |
| 372 | |
| 373 ContentSuggestion DownloadSuggestionsProvider::ConvertOfflinePage( | |
| 374 const OfflinePageItem& offline_page) const { | |
| 375 // TODO(vitaliii): Make sure the URL is actually opened as an offline URL even | |
| 376 // when the user is online. | |
|
Marc Treib
2016/10/11 12:07:40
Do we have a bug for this? If not, could you file
vitaliii
2016/10/13 08:43:06
Done.
Why do not you like
https://cs.chromium.org
Marc Treib
2016/10/13 12:11:25
TODOs in the code aren't a good issue tracking sys
vitaliii
2016/10/15 18:36:29
I see, good point.
| |
| 377 ContentSuggestion suggestion( | |
| 378 ContentSuggestion::ID(provided_category_, GetOfflinePagePerCategoryID( | |
| 379 offline_page.offline_id)), | |
| 380 offline_page.url); | |
| 381 | |
| 382 if (offline_page.title.empty()) { | |
| 383 // TODO(vitaliii): Remove this fallback once the OfflinePageModel provides | |
| 384 // titles for all (relevant) OfflinePageItems. | |
| 385 suggestion.set_title(base::UTF8ToUTF16(offline_page.url.spec())); | |
| 386 } else { | |
| 387 suggestion.set_title(offline_page.title); | |
| 388 } | |
| 389 suggestion.set_publish_date(offline_page.creation_time); | |
| 390 suggestion.set_publisher_name(base::UTF8ToUTF16(offline_page.url.host())); | |
| 391 return suggestion; | |
| 392 } | |
| 393 | |
| 394 ContentSuggestion DownloadSuggestionsProvider::ConvertDownloadItem( | |
| 395 const DownloadItem* download_item) const { | |
| 396 ContentSuggestion::ID per_category_id( | |
|
Marc Treib
2016/10/11 12:07:40
This is not a per-category-ID. But I'd just inline
vitaliii
2016/10/13 08:43:05
Done.
| |
| 397 provided_category_, | |
| 398 GetAssetDownloadPerCategoryID(download_item->GetId())); | |
| 399 // TODO(vitaliii): Ensure that files are opened in browser, but not downloaded | |
| 400 // again. | |
| 401 ContentSuggestion suggestion( | |
| 402 per_category_id, | |
| 403 net::FilePathToFileURL(download_item->GetTargetFilePath())); | |
| 404 // TODO(vitaliii): Set proper title. | |
| 405 suggestion.set_title( | |
| 406 base::UTF8ToUTF16(download_item->GetTargetFilePath().BaseName().value())); | |
|
Marc Treib
2016/10/11 12:07:40
I think this won't work on all platforms, since pa
vitaliii
2016/10/13 08:43:06
This is Anroid only for now, added DCHECK for futu
| |
| 407 suggestion.set_publish_date(download_item->GetEndTime()); | |
| 408 suggestion.set_publisher_name( | |
| 409 base::UTF8ToUTF16(download_item->GetURL().host())); | |
| 410 // TODO(vitaliii): Set suggestion icon. | |
| 411 return suggestion; | |
| 412 } | |
| 413 | |
| 414 bool DownloadSuggestionsProvider::CacheAssetDownloadIfNeeded( | |
| 415 const content::DownloadItem* item) { | |
| 416 if (!IsDownloadCompleted(item)) | |
| 417 return false; | |
| 418 | |
| 419 std::vector<const content::DownloadItem*>& cache = cached_asset_downloads_; | |
|
Marc Treib
2016/10/11 12:07:41
Please avoid non-const references in general.
Here
vitaliii
2016/10/13 08:43:06
Done.
| |
| 420 if (std::find(cache.begin(), cache.end(), item) != cache.end()) | |
|
Marc Treib
2016/10/11 12:07:41
optional: base::ContainsValue might be a bit easie
vitaliii
2016/10/13 08:43:07
Done.
| |
| 421 return false; | |
| 422 | |
| 423 std::set<std::string> dismissed_ids = ReadAssetDismissedIDsFromPrefs(); | |
| 424 if (dismissed_ids.count(GetAssetDownloadPerCategoryID(item->GetId()))) | |
| 425 return false; | |
| 426 | |
| 427 DCHECK(cache.size() <= kMaxSuggestionsCount); | |
|
Marc Treib
2016/10/11 12:07:40
DCHECK_LE?
vitaliii
2016/10/13 08:43:06
Done.
| |
| 428 if (cache.size() == kMaxSuggestionsCount) { | |
| 429 std::vector<const content::DownloadItem*>::iterator oldest = | |
|
Marc Treib
2016/10/11 12:07:40
optional: auto oldest_it = ...?
vitaliii
2016/10/13 08:43:05
Done.
| |
| 430 std::max_element(cache.begin(), cache.end(), | |
| 431 OrderDownloadsMostRecentlyDownloadedCompletedFirst()); | |
| 432 if (item->GetEndTime() <= (*oldest)->GetEndTime()) | |
|
vitaliii
2016/10/11 08:15:56
Would using an index here be better?
Marc Treib
2016/10/11 12:07:41
I don't follow - an index for what?
vitaliii
2016/10/13 08:43:06
i.e.
int i = oldest - cached_asset_downloads_.beg
Marc Treib
2016/10/13 12:11:25
Ah - no, what you have is fine.
vitaliii
2016/10/15 18:36:29
Acknowledged.
| |
| 433 return false; | |
| 434 | |
| 435 (*oldest) = item; | |
|
Marc Treib
2016/10/11 12:07:41
nit: parens not required
vitaliii
2016/10/13 08:43:05
Done.
| |
| 436 } else { | |
| 437 cache.push_back(item); | |
| 438 } | |
| 439 | |
| 440 return true; | |
| 441 } | |
| 442 | |
| 443 bool DownloadSuggestionsProvider::RemoveSuggestionFromCacheIfPresent( | |
| 444 const ContentSuggestion::ID& suggestion_id) { | |
| 445 DCHECK_EQ(provided_category_, suggestion_id.category()); | |
| 446 if (IsOfflinePageID(suggestion_id)) { | |
| 447 std::vector<offline_pages::OfflinePageItem>& cache = | |
|
Marc Treib
2016/10/11 12:07:41
As above: Please spell out cached_offline_page_dow
vitaliii
2016/10/13 08:43:06
Done.
| |
| 448 cached_offline_page_downloads_; | |
| 449 for (int i = 0; i < static_cast<int>(cache.size()); ++i) { | |
|
Marc Treib
2016/10/11 12:07:40
optional: This might be slightly nicer as an std::
vitaliii
2016/10/13 08:43:06
Done.
| |
| 450 if (GetOfflinePagePerCategoryID(cache[i].offline_id) == | |
| 451 suggestion_id.id_within_category()) { | |
| 452 cache.erase(cache.begin() + i); | |
| 453 return true; | |
| 454 } | |
| 455 } | |
| 456 return false; | |
| 457 } else { | |
|
Marc Treib
2016/10/11 12:07:40
No "else" after "return"
vitaliii
2016/10/13 08:43:06
Done.
| |
| 458 std::vector<const content::DownloadItem*>& cache = cached_asset_downloads_; | |
|
Marc Treib
2016/10/11 12:07:40
Also here: Please spell out
vitaliii
2016/10/13 08:43:05
Done.
| |
| 459 for (int i = 0; i < static_cast<int>(cache.size()); ++i) { | |
| 460 if (GetAssetDownloadPerCategoryID(cache[i]->GetId()) == | |
| 461 suggestion_id.id_within_category()) { | |
| 462 cache.erase(cache.begin() + i); | |
| 463 return true; | |
| 464 } | |
| 465 } | |
| 466 return false; | |
| 467 } | |
| 468 } | |
| 469 | |
| 470 void DownloadSuggestionsProvider:: | |
| 471 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded( | |
| 472 const ContentSuggestion::ID& suggestion_id) { | |
| 473 DCHECK_EQ(provided_category_, suggestion_id.category()); | |
| 474 if (RemoveSuggestionFromCacheIfPresent(suggestion_id)) { | |
|
Marc Treib
2016/10/11 12:07:41
if (!Remove...)
return;
and save a level of inde
vitaliii
2016/10/13 08:43:07
Done.
| |
| 475 if (IsOfflinePageID(suggestion_id)) { | |
| 476 if (cached_offline_page_downloads_.size() == kMaxSuggestionsCount - 1) { | |
| 477 // Previously there were |kMaxSuggestionsCount| cached suggestion, | |
| 478 // therefore, overall there may be more than |kMaxSuggestionsCount| | |
| 479 // suggestions in the model and now one of them may be cached instead of | |
| 480 // the removed one. Even though, the suggestions are not immediately | |
| 481 // used the cache has to be kept up to date, because it may be used when | |
| 482 // other data source is updated. | |
| 483 FetchOfflinePagesDownloads(); | |
| 484 } | |
| 485 } else { | |
| 486 if (cached_asset_downloads_.size() == kMaxSuggestionsCount - 1) { | |
| 487 // The same as the case above. | |
| 488 FetchAssetsDownloads(); | |
| 489 } | |
| 490 } | |
| 491 } | |
| 492 } | |
| 493 | |
| 494 void DownloadSuggestionsProvider::ProcessAllOfflinePages( | |
| 495 const std::vector<offline_pages::OfflinePageItem>& all_offline_pages) { | |
| 496 std::set<std::string> old_dismissed_ids = | |
| 497 ReadOfflinePageDismissedIDsFromPrefs(); | |
| 498 std::set<std::string> new_dismissed_ids; | |
| 499 std::vector<const OfflinePageItem*> items; | |
| 500 // Filtering out dismissed items and pruning dismissed IDs. | |
| 501 for (const OfflinePageItem& item : all_offline_pages) { | |
| 502 if (!IsOfflinePageDownload(item.client_id)) | |
| 503 continue; | |
| 504 | |
| 505 std::string per_category_id = GetOfflinePagePerCategoryID(item.offline_id); | |
| 506 if (!old_dismissed_ids.count(per_category_id)) | |
|
Marc Treib
2016/10/11 12:07:41
I think the dismissed IDs sometimes have the "O"/"
vitaliii
2016/10/13 08:43:05
For the former - I do not think so, I added DCHECK
Marc Treib
2016/10/13 12:11:25
I looked over all the code, and it looks like you
vitaliii
2016/10/15 18:36:29
Let's stick with this approach for now and then I
| |
| 507 items.push_back(&item); | |
| 508 else | |
| 509 new_dismissed_ids.insert(per_category_id); | |
| 510 } | |
| 511 | |
| 512 if (static_cast<int>(items.size()) > kMaxSuggestionsCount) { | |
| 513 std::nth_element(items.begin(), items.begin() + kMaxSuggestionsCount, | |
| 514 items.end(), | |
| 515 OrderOfflinePagesByMostRecentlyVisitedFirst()); | |
|
Marc Treib
2016/10/11 12:07:41
You could use a lambda here
vitaliii
2016/10/13 08:43:04
Done.
| |
| 516 items.resize(kMaxSuggestionsCount); | |
| 517 } | |
| 518 | |
| 519 cached_offline_page_downloads_.clear(); | |
| 520 for (const OfflinePageItem* item : items) { | |
| 521 cached_offline_page_downloads_.push_back(*item); | |
| 522 } | |
| 523 | |
| 524 if (old_dismissed_ids.size() != new_dismissed_ids.size()) { | |
| 525 StoreOfflinePageDismissedIDsToPrefs(new_dismissed_ids); | |
| 526 } | |
| 527 } | |
| 528 | |
| 529 void DownloadSuggestionsProvider::InvalidateSuggestion( | |
| 530 const std::string& per_category_id) { | |
| 531 ContentSuggestion::ID suggestion_id(provided_category_, per_category_id); | |
| 532 observer()->OnSuggestionInvalidated(this, suggestion_id); | |
| 533 | |
| 534 std::set<std::string> dismissed_ids = | |
| 535 ReadDismissedIDsFromPrefs(suggestion_id); | |
| 536 auto it = dismissed_ids.find(per_category_id); | |
| 537 if (it != dismissed_ids.end()) { | |
| 538 dismissed_ids.erase(it); | |
| 539 StoreDismissedIDsToPrefs(suggestion_id, dismissed_ids); | |
| 540 } | |
| 541 | |
| 542 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded(suggestion_id); | |
| 543 } | |
| 544 | |
| 545 std::set<std::string> | |
| 546 DownloadSuggestionsProvider::ReadAssetDismissedIDsFromPrefs() const { | |
| 547 return prefs::ReadDismissedIDsFromPrefs( | |
| 548 *pref_service_, prefs::kDismissedAssetDownloadSuggestions); | |
| 549 } | |
| 550 | |
| 551 void DownloadSuggestionsProvider::StoreAssetDismissedIDsToPrefs( | |
| 552 const std::set<std::string>& dismissed_ids) { | |
| 553 prefs::StoreDismissedIDsToPrefs( | |
| 554 pref_service_, prefs::kDismissedAssetDownloadSuggestions, dismissed_ids); | |
| 555 } | |
| 556 | |
| 557 std::set<std::string> | |
| 558 DownloadSuggestionsProvider::ReadOfflinePageDismissedIDsFromPrefs() const { | |
| 559 return prefs::ReadDismissedIDsFromPrefs( | |
| 560 *pref_service_, prefs::kDismissedOfflinePageDownloadSuggestions); | |
| 561 } | |
| 562 | |
| 563 void DownloadSuggestionsProvider::StoreOfflinePageDismissedIDsToPrefs( | |
| 564 const std::set<std::string>& dismissed_ids) { | |
| 565 prefs::StoreDismissedIDsToPrefs( | |
| 566 pref_service_, prefs::kDismissedOfflinePageDownloadSuggestions, | |
| 567 dismissed_ids); | |
| 568 } | |
| 569 | |
| 570 std::set<std::string> DownloadSuggestionsProvider::ReadDismissedIDsFromPrefs( | |
| 571 const ContentSuggestion::ID& suggestion_id) const { | |
| 572 if (IsOfflinePageID(suggestion_id)) { | |
| 573 return ReadOfflinePageDismissedIDsFromPrefs(); | |
| 574 } else { | |
|
Marc Treib
2016/10/11 12:07:40
nit: No "else" after "return"
Or maybe just get r
vitaliii
2016/10/13 08:43:06
Done.
| |
| 575 return ReadAssetDismissedIDsFromPrefs(); | |
| 576 } | |
| 577 } | |
| 578 | |
| 579 void DownloadSuggestionsProvider::StoreDismissedIDsToPrefs( | |
| 580 const ContentSuggestion::ID& suggestion_id, | |
| 581 const std::set<std::string>& dismissed_ids) { | |
| 582 if (IsOfflinePageID(suggestion_id)) { | |
| 583 StoreOfflinePageDismissedIDsToPrefs(dismissed_ids); | |
| 584 } else { | |
|
Marc Treib
2016/10/11 12:07:39
Also here
vitaliii
2016/10/13 08:43:05
Done.
| |
| 585 StoreAssetDismissedIDsToPrefs(dismissed_ids); | |
| 586 } | |
| 587 } | |
| 588 | |
| 589 } // namespace ntp_snippets | |
| OLD | NEW |