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 "components/offline_pages/core/prefetch/prefetch_dispatcher_impl.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/guid.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/task/post_task.h"
15 #include "base/task_runner.h"
16 #include "base/threading/thread_task_runner_handle.h"
17 #include "components/offline_pages/core/client_namespace_constants.h"
18 #include "components/offline_pages/core/offline_event_logger.h"
19 #include "components/offline_pages/core/offline_page_feature.h"
20 #include "components/offline_pages/core/offline_page_model.h"
21 #include "components/offline_pages/core/prefetch/offline_metrics_collector.h"
22 #include "components/offline_pages/core/prefetch/prefetch_background_task.h"
23 #include "components/offline_pages/core/prefetch/prefetch_background_task_handler.h"
24 #include "components/offline_pages/core/prefetch/prefetch_downloader.h"
25 #include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h"
26 #include "components/offline_pages/core/prefetch/prefetch_importer.h"
27 #include "components/offline_pages/core/prefetch/prefetch_network_request_factory.h"
28 #include "components/offline_pages/core/prefetch/prefetch_prefs.h"
29 #include "components/offline_pages/core/prefetch/prefetch_service.h"
30 #include "components/offline_pages/core/prefetch/prefetch_types.h"
31 #include "components/offline_pages/core/prefetch/server_forbidden_check_request.h"
32 #include "components/offline_pages/core/prefetch/suggestions_provider.h"
33 #include "components/offline_pages/core/prefetch/tasks/add_unique_urls_task.h"
34 #include "components/offline_pages/core/prefetch/tasks/download_archives_task.h"
35 #include "components/offline_pages/core/prefetch/tasks/download_cleanup_task.h"
36 #include "components/offline_pages/core/prefetch/tasks/download_completed_task.h"
37 #include "components/offline_pages/core/prefetch/tasks/finalize_dismissed_url_suggestion_task.h"
38 #include "components/offline_pages/core/prefetch/tasks/generate_page_bundle_reconcile_task.h"
39 #include "components/offline_pages/core/prefetch/tasks/generate_page_bundle_task.h"
40 #include "components/offline_pages/core/prefetch/tasks/get_operation_task.h"
41 #include "components/offline_pages/core/prefetch/tasks/import_archives_task.h"
42 #include "components/offline_pages/core/prefetch/tasks/import_cleanup_task.h"
43 #include "components/offline_pages/core/prefetch/tasks/import_completed_task.h"
44 #include "components/offline_pages/core/prefetch/tasks/mark_operation_done_task.h"
45 #include "components/offline_pages/core/prefetch/tasks/metrics_finalization_task.h"
46 #include "components/offline_pages/core/prefetch/tasks/page_bundle_update_task.h"
47 #include "components/offline_pages/core/prefetch/tasks/remove_url_task.h"
48 #include "components/offline_pages/core/prefetch/tasks/sent_get_operation_cleanup_task.h"
49 #include "components/offline_pages/core/prefetch/tasks/stale_entry_finalizer_task.h"
50 #include "components/offline_pages/core/prefetch/visuals_fetch_by_url.h"
51 #include "components/prefs/pref_service.h"
52 #include "url/gurl.h"
53
54 namespace offline_pages {
55
56 namespace {
57
DeleteBackgroundTaskHelper(std::unique_ptr<PrefetchBackgroundTask> task)58 void DeleteBackgroundTaskHelper(std::unique_ptr<PrefetchBackgroundTask> task) {
59 task.reset();
60 }
61
SuggestionToPrefetchURL(PrefetchSuggestion suggestion)62 PrefetchURL SuggestionToPrefetchURL(PrefetchSuggestion suggestion) {
63 return PrefetchURL(suggestion.article_url.spec(), suggestion.article_url,
64 base::UTF8ToUTF16(suggestion.article_title),
65 suggestion.thumbnail_url, suggestion.favicon_url,
66 suggestion.article_snippet,
67 suggestion.article_attribution);
68 }
69
70 } // namespace
71
PrefetchDispatcherImpl(PrefService * pref_service)72 PrefetchDispatcherImpl::PrefetchDispatcherImpl(PrefService* pref_service)
73 : pref_service_(pref_service), task_queue_(this) {}
74
75 PrefetchDispatcherImpl::~PrefetchDispatcherImpl() = default;
76
SetService(PrefetchService * service)77 void PrefetchDispatcherImpl::SetService(PrefetchService* service) {
78 CHECK(service);
79 service_ = service;
80 }
81
SchedulePipelineProcessing()82 void PrefetchDispatcherImpl::SchedulePipelineProcessing() {
83 needs_pipeline_processing_ = true;
84 service_->GetLogger()->RecordActivity(
85 "Dispatcher: Scheduled more pipeline processing.");
86 }
87
EnsureTaskScheduled()88 void PrefetchDispatcherImpl::EnsureTaskScheduled() {
89 if (background_task_) {
90 background_task_->SetReschedule(
91 PrefetchBackgroundTaskRescheduleType::RESCHEDULE_WITHOUT_BACKOFF);
92 } else {
93 service_->GetPrefetchBackgroundTaskHandler()->EnsureTaskScheduled();
94 }
95 }
96
AddCandidatePrefetchURLs(const std::string & name_space,const std::vector<PrefetchURL> & prefetch_urls)97 void PrefetchDispatcherImpl::AddCandidatePrefetchURLs(
98 const std::string& name_space,
99 const std::vector<PrefetchURL>& prefetch_urls) {
100 if (!prefetch_prefs::IsEnabled(pref_service_)) {
101 if (prefetch_prefs::IsForbiddenCheckDue(pref_service_))
102 CheckIfEnabledByServer(pref_service_, service_);
103 return;
104 }
105
106 service_->GetLogger()->RecordActivity("Dispatcher: Received " +
107 std::to_string(prefetch_urls.size()) +
108 " suggested URLs.");
109
110 PrefetchStore* prefetch_store = service_->GetPrefetchStore();
111
112 // Run 2 pipeline expiration tasks first to ensure there is no buildup of URLs
113 // in the pipeline if the new ones are coming but NOW can't be entered (for
114 // example, if the user is never on WiFi with enough battery charge).
115 // First, detect stale entries and move them to FINISHED.
116 task_queue_.AddTask(
117 std::make_unique<StaleEntryFinalizerTask>(this, prefetch_store));
118
119 // Second, move FINISHED to ZOMBIE.
120 task_queue_.AddTask(
121 std::make_unique<MetricsFinalizationTask>(prefetch_store));
122
123 // Third, add new unique URLs and remove unneeded ZOMBIEs.
124 std::unique_ptr<Task> add_task = std::make_unique<AddUniqueUrlsTask>(
125 this, prefetch_store, name_space, prefetch_urls);
126 task_queue_.AddTask(std::move(add_task));
127
128 // Report the 'enabled' day if we receive URLs and Prefetch is enabled.
129 service_->GetOfflineMetricsCollector()->OnPrefetchEnabled();
130 }
131
NewSuggestionsAvailable(SuggestionsProvider * suggestions_provider)132 void PrefetchDispatcherImpl::NewSuggestionsAvailable(
133 SuggestionsProvider* suggestions_provider) {
134 // No need to GetKnownContent if prefetching is disabled (however, we don't
135 // want to prevent the server-forbidden check).
136 if (!prefetch_prefs::IsEnabled(pref_service_)) {
137 if (prefetch_prefs::IsForbiddenCheckDue(pref_service_))
138 CheckIfEnabledByServer(pref_service_, service_);
139 return;
140 }
141
142 suggestions_provider->GetCurrentArticleSuggestions(
143 base::BindOnce(&PrefetchDispatcherImpl::AddSuggestions, GetWeakPtr()));
144 }
145
RemoveSuggestion(const GURL & url)146 void PrefetchDispatcherImpl::RemoveSuggestion(const GURL& url) {
147 if (!prefetch_prefs::IsEnabled(pref_service_))
148 return;
149
150 // Remove the URL from the prefetch database.
151 task_queue_.AddTask(MakeRemoveUrlTask(service_->GetPrefetchStore(), url));
152
153 // Remove the URL from the offline model.
154 PageCriteria criteria;
155 criteria.url = url;
156 criteria.client_namespaces =
157 std::vector<std::string>{kSuggestedArticlesNamespace};
158 service_->GetOfflinePageModel()->DeletePagesWithCriteria(criteria,
159 base::DoNothing());
160 }
161
RemoveAllUnprocessedPrefetchURLs(const std::string & name_space)162 void PrefetchDispatcherImpl::RemoveAllUnprocessedPrefetchURLs(
163 const std::string& name_space) {
164 if (!prefetch_prefs::IsEnabled(pref_service_))
165 return;
166
167 NOTIMPLEMENTED();
168 }
169
RemovePrefetchURLsByClientId(const ClientId & client_id)170 void PrefetchDispatcherImpl::RemovePrefetchURLsByClientId(
171 const ClientId& client_id) {
172 if (!prefetch_prefs::IsEnabled(pref_service_))
173 return;
174 PrefetchStore* prefetch_store = service_->GetPrefetchStore();
175 task_queue_.AddTask(std::make_unique<FinalizeDismissedUrlSuggestionTask>(
176 prefetch_store, client_id));
177 }
178
BeginBackgroundTask(std::unique_ptr<PrefetchBackgroundTask> background_task)179 void PrefetchDispatcherImpl::BeginBackgroundTask(
180 std::unique_ptr<PrefetchBackgroundTask> background_task) {
181 if (!prefetch_prefs::IsEnabled(pref_service_))
182 return;
183 service_->GetLogger()->RecordActivity(
184 "Dispatcher: Beginning background task.");
185
186 background_task_ = std::move(background_task);
187 service_->GetPrefetchBackgroundTaskHandler()->RemoveSuspension();
188
189 // Reset suspended state in case that it was set last time and Chrome is still
190 // running till new background task starts after the suspension period.
191 suspended_ = false;
192
193 QueueReconcileTasks();
194 QueueActionTasks();
195 }
196
QueueReconcileTasks()197 void PrefetchDispatcherImpl::QueueReconcileTasks() {
198 if (suspended_)
199 return;
200
201 service_->GetLogger()->RecordActivity("Dispatcher: Adding reconcile tasks.");
202 // Note: For optimal results StaleEntryFinalizerTask should be executed before
203 // other reconciler tasks that deal with external systems so that entries
204 // finalized by it will promptly effect any external processing they relate
205 // to.
206 task_queue_.AddTask(std::make_unique<StaleEntryFinalizerTask>(
207 this, service_->GetPrefetchStore()));
208
209 task_queue_.AddTask(std::make_unique<GeneratePageBundleReconcileTask>(
210 service_->GetPrefetchStore(),
211 service_->GetPrefetchNetworkRequestFactory()));
212
213 task_queue_.AddTask(std::make_unique<SentGetOperationCleanupTask>(
214 service_->GetPrefetchStore(),
215 service_->GetPrefetchNetworkRequestFactory()));
216
217 // Notifies the downloader that the download cleanup can proceed when the
218 // download service is up. The prefetch service and download service are two
219 // separate services which can start up on their own. The download cleanup
220 // should only kick in when both services are ready.
221 service_->GetPrefetchDownloader()->CleanupDownloadsWhenReady();
222
223 task_queue_.AddTask(std::make_unique<ImportCleanupTask>(
224 service_->GetPrefetchStore(), service_->GetPrefetchImporter()));
225
226 // This task should be last, because it is least important for correct
227 // operation of the system, and because any reconciliation tasks might
228 // generate more entries in the FINISHED state that the finalization task
229 // could pick up.
230 task_queue_.AddTask(
231 std::make_unique<MetricsFinalizationTask>(service_->GetPrefetchStore()));
232 }
233
QueueActionTasks()234 void PrefetchDispatcherImpl::QueueActionTasks() {
235 service_->GetLogger()->RecordActivity("Dispatcher: Adding action tasks.");
236
237 // Import should be run first to minimize time to import after download
238 // finishes, during the download background task.
239 std::unique_ptr<Task> import_archives_task =
240 std::make_unique<ImportArchivesTask>(service_->GetPrefetchStore(),
241 service_->GetPrefetchImporter());
242 task_queue_.AddTask(std::move(import_archives_task));
243
244 std::unique_ptr<Task> download_archives_task =
245 std::make_unique<DownloadArchivesTask>(service_->GetPrefetchStore(),
246 service_->GetPrefetchDownloader(),
247 pref_service_);
248 task_queue_.AddTask(std::move(download_archives_task));
249
250 // The following tasks should not be run unless we are in the background task,
251 // as we need to ensure WiFi access at that time.
252 if (!background_task_)
253 return;
254 DCHECK(!service_->GetCachedGCMToken().empty());
255
256 std::unique_ptr<Task> get_operation_task = std::make_unique<GetOperationTask>(
257 service_->GetPrefetchStore(),
258 service_->GetPrefetchNetworkRequestFactory(),
259 base::BindRepeating(
260 &PrefetchDispatcherImpl::DidGenerateBundleOrGetOperationRequest,
261 GetWeakPtr(), "GetOperationRequest"));
262 task_queue_.AddTask(std::move(get_operation_task));
263
264 std::unique_ptr<Task> generate_page_bundle_task =
265 std::make_unique<GeneratePageBundleTask>(
266 this, service_->GetPrefetchStore(), service_->GetCachedGCMToken(),
267 service_->GetPrefetchNetworkRequestFactory(),
268 base::BindRepeating(
269 &PrefetchDispatcherImpl::DidGenerateBundleOrGetOperationRequest,
270 GetWeakPtr(), "GeneratePageBundleRequest"));
271 task_queue_.AddTask(std::move(generate_page_bundle_task));
272 }
273
AddSuggestions(std::vector<PrefetchSuggestion> suggestions)274 void PrefetchDispatcherImpl::AddSuggestions(
275 std::vector<PrefetchSuggestion> suggestions) {
276 std::vector<PrefetchURL> urls;
277 urls.reserve(suggestions.size());
278
279 for (auto& suggestion : suggestions) {
280 urls.push_back(SuggestionToPrefetchURL(std::move(suggestion)));
281 }
282 AddCandidatePrefetchURLs(kSuggestedArticlesNamespace, urls);
283 }
284
StopBackgroundTask()285 void PrefetchDispatcherImpl::StopBackgroundTask() {
286 if (!prefetch_prefs::IsEnabled(pref_service_))
287 return;
288
289 service_->GetLogger()->RecordActivity(
290 "Dispatcher: Stopping background task.");
291
292 DisposeTask();
293 }
294
OnTaskQueueIsIdle()295 void PrefetchDispatcherImpl::OnTaskQueueIsIdle() {
296 if (!suspended_ && needs_pipeline_processing_) {
297 needs_pipeline_processing_ = false;
298 QueueActionTasks();
299 } else {
300 PrefetchNetworkRequestFactory* request_factory =
301 service_->GetPrefetchNetworkRequestFactory();
302 if (!request_factory->HasOutstandingRequests())
303 DisposeTask();
304 }
305 }
306
DisposeTask()307 void PrefetchDispatcherImpl::DisposeTask() {
308 if (!background_task_)
309 return;
310
311 // Delay the deletion till the caller finishes.
312 base::ThreadTaskRunnerHandle::Get()->PostTask(
313 FROM_HERE,
314 base::BindOnce(&DeleteBackgroundTaskHelper, std::move(background_task_)));
315 }
316
GCMOperationCompletedMessageReceived(const std::string & operation_name)317 void PrefetchDispatcherImpl::GCMOperationCompletedMessageReceived(
318 const std::string& operation_name) {
319 if (!prefetch_prefs::IsEnabled(pref_service_))
320 return;
321
322 service_->GetLogger()->RecordActivity("Dispatcher: Received GCM message.");
323
324 PrefetchStore* prefetch_store = service_->GetPrefetchStore();
325 task_queue_.AddTask(std::make_unique<MarkOperationDoneTask>(
326 this, prefetch_store, operation_name));
327 }
328
DidGenerateBundleOrGetOperationRequest(const std::string & request_name_for_logging,PrefetchRequestStatus status,const std::string & operation_name,const std::vector<RenderPageInfo> & pages)329 void PrefetchDispatcherImpl::DidGenerateBundleOrGetOperationRequest(
330 const std::string& request_name_for_logging,
331 PrefetchRequestStatus status,
332 const std::string& operation_name,
333 const std::vector<RenderPageInfo>& pages) {
334 LogRequestResult(request_name_for_logging, status, operation_name, pages);
335
336 // Note that we still want to trigger PageBundleUpdateTask even if the request
337 // fails and no page is returned. This is because currently we only check for
338 // the empty task queue and no outstanding request in order to decide whether
339 // to dispose th background task upon the completion of a task.
340 PrefetchStore* prefetch_store = service_->GetPrefetchStore();
341 task_queue_.AddTask(std::make_unique<PageBundleUpdateTask>(
342 prefetch_store, this, operation_name, pages));
343
344 if (background_task_ && status != PrefetchRequestStatus::kSuccess &&
345 status != PrefetchRequestStatus::kEmptyRequestSuccess) {
346 PrefetchBackgroundTaskRescheduleType reschedule_type =
347 PrefetchBackgroundTaskRescheduleType::NO_RESCHEDULE;
348 switch (status) {
349 case PrefetchRequestStatus::kShouldRetryWithBackoff:
350 reschedule_type =
351 PrefetchBackgroundTaskRescheduleType::RESCHEDULE_WITH_BACKOFF;
352 break;
353 case PrefetchRequestStatus::kShouldRetryWithoutBackoff:
354 reschedule_type =
355 PrefetchBackgroundTaskRescheduleType::RESCHEDULE_WITHOUT_BACKOFF;
356 break;
357 case PrefetchRequestStatus::kShouldSuspendForbiddenByOPS:
358 case PrefetchRequestStatus::kShouldSuspendNewlyForbiddenByOPS:
359 case PrefetchRequestStatus::kShouldSuspendForbidden:
360 case PrefetchRequestStatus::kShouldSuspendNotImplemented:
361 case PrefetchRequestStatus::kShouldSuspendBlockedByAdministrator:
362 reschedule_type = PrefetchBackgroundTaskRescheduleType::SUSPEND;
363 break;
364 case PrefetchRequestStatus::kSuccess:
365 case PrefetchRequestStatus::kEmptyRequestSuccess:
366 NOTREACHED();
367 break;
368 }
369 background_task_->SetReschedule(reschedule_type);
370
371 if (reschedule_type == PrefetchBackgroundTaskRescheduleType::SUSPEND)
372 suspended_ = true;
373 }
374 }
375
CleanupDownloads(const std::set<std::string> & outstanding_download_ids,const std::map<std::string,std::pair<base::FilePath,int64_t>> & success_downloads)376 void PrefetchDispatcherImpl::CleanupDownloads(
377 const std::set<std::string>& outstanding_download_ids,
378 const std::map<std::string, std::pair<base::FilePath, int64_t>>&
379 success_downloads) {
380 task_queue_.AddTask(std::make_unique<DownloadCleanupTask>(
381 this, service_->GetPrefetchStore(), outstanding_download_ids,
382 success_downloads));
383 }
384
GeneratePageBundleRequested(std::unique_ptr<PrefetchDispatcher::IdsVector> ids)385 void PrefetchDispatcherImpl::GeneratePageBundleRequested(
386 std::unique_ptr<PrefetchDispatcher::IdsVector> ids) {
387 // Reverse the order so that the fresher items are last. This is done because
388 // the ids are popped from the end of the vector.
389 std::reverse(ids->begin(), ids->end());
390 FetchVisuals(std::move(ids), /* is_first_attempt= */ true);
391 }
392
DownloadCompleted(const PrefetchDownloadResult & download_result)393 void PrefetchDispatcherImpl::DownloadCompleted(
394 const PrefetchDownloadResult& download_result) {
395 if (!prefetch_prefs::IsEnabled(pref_service_))
396 return;
397
398 service_->GetLogger()->RecordActivity(
399 "Download " + download_result.download_id +
400 (download_result.success ? "succeeded" : "failed"));
401 if (download_result.success) {
402 service_->GetLogger()->RecordActivity(
403 "Download size: " + std::to_string(download_result.file_size));
404 }
405
406 task_queue_.AddTask(std::make_unique<DownloadCompletedTask>(
407 this, service_->GetPrefetchStore(), download_result));
408 task_queue_.AddTask(std::make_unique<ImportArchivesTask>(
409 service_->GetPrefetchStore(), service_->GetPrefetchImporter()));
410 }
411
ItemDownloaded(int64_t offline_id,const ClientId & client_id)412 void PrefetchDispatcherImpl::ItemDownloaded(int64_t offline_id,
413 const ClientId& client_id) {
414 auto ids = std::make_unique<IdsVector>();
415 ids->emplace_back(offline_id, client_id);
416 FetchVisuals(std::move(ids), /* is_first_attempt= */ false);
417 }
418
ArchiveImported(int64_t offline_id,bool success)419 void PrefetchDispatcherImpl::ArchiveImported(int64_t offline_id, bool success) {
420 DCHECK_NE(OfflinePageModel::kInvalidOfflineId, offline_id);
421
422 if (!prefetch_prefs::IsEnabled(pref_service_))
423 return;
424
425 service_->GetLogger()->RecordActivity("Importing archive " +
426 std::to_string(offline_id) +
427 (success ? "succeeded" : "failed"));
428
429 if (success)
430 service_->GetOfflineMetricsCollector()->OnSuccessfulPagePrefetch();
431
432 task_queue_.AddTask(std::make_unique<ImportCompletedTask>(
433 this, service_->GetPrefetchStore(), service_->GetPrefetchImporter(),
434 offline_id, success));
435 }
436
LogRequestResult(const std::string & request_name_for_logging,PrefetchRequestStatus status,const std::string & operation_name,const std::vector<RenderPageInfo> & pages)437 void PrefetchDispatcherImpl::LogRequestResult(
438 const std::string& request_name_for_logging,
439 PrefetchRequestStatus status,
440 const std::string& operation_name,
441 const std::vector<RenderPageInfo>& pages) {
442 service_->GetLogger()->RecordActivity(
443 "Finished " + request_name_for_logging +
444 " for operation: " + operation_name +
445 " with status: " + std::to_string(static_cast<int>(status)) +
446 "; included " + std::to_string(pages.size()) + " pages in result.");
447 for (const RenderPageInfo& page : pages) {
448 service_->GetLogger()->RecordActivity(
449 "Response for page: " + page.url +
450 "; status=" + std::to_string(static_cast<int>(page.status)));
451 }
452 }
453
FetchVisuals(std::unique_ptr<PrefetchDispatcher::IdsVector> remaining_ids,bool is_first_attempt)454 void PrefetchDispatcherImpl::FetchVisuals(
455 std::unique_ptr<PrefetchDispatcher::IdsVector> remaining_ids,
456 bool is_first_attempt) {
457 if (remaining_ids->empty())
458 return;
459
460 int64_t offline_id = remaining_ids->back().first;
461 ClientId client_id = std::move(remaining_ids->back().second);
462 DCHECK(client_id.name_space == kSuggestedArticlesNamespace);
463 remaining_ids->pop_back();
464
465 service_->GetOfflinePageModel()->GetVisualsAvailability(
466 offline_id,
467 base::BindOnce(&PrefetchDispatcherImpl::VisualsAvailabilityChecked,
468 GetWeakPtr(), offline_id, std::move(client_id),
469 std::move(remaining_ids), is_first_attempt));
470 }
471
VisualsAvailabilityChecked(int64_t offline_id,ClientId client_id,std::unique_ptr<PrefetchDispatcher::IdsVector> remaining_ids,bool is_first_attempt,VisualsAvailability availability)472 void PrefetchDispatcherImpl::VisualsAvailabilityChecked(
473 int64_t offline_id,
474 ClientId client_id,
475 std::unique_ptr<PrefetchDispatcher::IdsVector> remaining_ids,
476 bool is_first_attempt,
477 VisualsAvailability availability) {
478 if (availability.has_thumbnail && availability.has_favicon) {
479 FetchVisuals(std::move(remaining_ids), is_first_attempt);
480 } else {
481 task_queue_.AddTask(std::make_unique<GetVisualsInfoTask>(
482 service_->GetPrefetchStore(), offline_id,
483 base::BindOnce(&PrefetchDispatcherImpl::VisualsInfoReceived,
484 GetWeakPtr(), offline_id, std::move(remaining_ids),
485 is_first_attempt, availability)));
486 }
487 }
488
VisualsInfoReceived(int64_t offline_id,std::unique_ptr<IdsVector> remaining_ids,bool is_first_attempt,VisualsAvailability availability,GetVisualsInfoTask::Result result)489 void PrefetchDispatcherImpl::VisualsInfoReceived(
490 int64_t offline_id,
491 std::unique_ptr<IdsVector> remaining_ids,
492 bool is_first_attempt,
493 VisualsAvailability availability,
494 GetVisualsInfoTask::Result result) {
495 GURL favicon_url = availability.has_favicon || result.favicon_url.is_empty()
496 ? GURL()
497 : result.favicon_url;
498
499 if (!availability.has_thumbnail && !result.thumbnail_url.is_empty()) {
500 FetchThumbnailByURL(
501 base::BindOnce(&PrefetchDispatcherImpl::ThumbnailFetchComplete,
502 GetWeakPtr(), offline_id, std::move(remaining_ids),
503 is_first_attempt, favicon_url),
504 service_->GetImageFetcher(), result.thumbnail_url);
505 } else if (!favicon_url.is_empty()) {
506 FetchFavicon(offline_id, std::move(remaining_ids), is_first_attempt,
507 favicon_url);
508 } else {
509 FetchVisuals(std::move(remaining_ids), is_first_attempt);
510 }
511 }
512
ThumbnailFetchComplete(int64_t offline_id,std::unique_ptr<IdsVector> remaining_ids,bool is_first_attempt,const GURL & favicon_url,const std::string & thumbnail)513 void PrefetchDispatcherImpl::ThumbnailFetchComplete(
514 int64_t offline_id,
515 std::unique_ptr<IdsVector> remaining_ids,
516 bool is_first_attempt,
517 const GURL& favicon_url,
518 const std::string& thumbnail) {
519 if (!thumbnail.empty())
520 service_->GetOfflinePageModel()->StoreThumbnail(offline_id, thumbnail);
521
522 if (favicon_url.is_empty()) {
523 FetchVisuals(std::move(remaining_ids), is_first_attempt);
524 } else {
525 FetchFavicon(offline_id, std::move(remaining_ids), is_first_attempt,
526 favicon_url);
527 }
528 }
529
FetchFavicon(int64_t offline_id,std::unique_ptr<IdsVector> remaining_ids,bool is_first_attempt,const GURL & favicon_url)530 void PrefetchDispatcherImpl::FetchFavicon(
531 int64_t offline_id,
532 std::unique_ptr<IdsVector> remaining_ids,
533 bool is_first_attempt,
534 const GURL& favicon_url) {
535 FetchFaviconByURL(
536 base::BindOnce(&PrefetchDispatcherImpl::FaviconFetchComplete,
537 GetWeakPtr(), offline_id, std::move(remaining_ids),
538 is_first_attempt),
539 service_->GetImageFetcher(), favicon_url);
540 }
541
FaviconFetchComplete(int64_t offline_id,std::unique_ptr<IdsVector> remaining_ids,bool is_first_attempt,const std::string & favicon_data)542 void PrefetchDispatcherImpl::FaviconFetchComplete(
543 int64_t offline_id,
544 std::unique_ptr<IdsVector> remaining_ids,
545 bool is_first_attempt,
546 const std::string& favicon_data) {
547 if (!favicon_data.empty()) {
548 service_->GetOfflinePageModel()->StoreFavicon(offline_id, favicon_data);
549 }
550
551 FetchVisuals(std::move(remaining_ids), is_first_attempt);
552 }
553
554 } // namespace offline_pages
555