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 "chrome/browser/background_fetch/background_fetch_download_client.h"
6 
7 #include <memory>
8 #include <set>
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/threading/sequenced_task_runner_handle.h"
13 #include "chrome/browser/background_fetch/background_fetch_delegate_impl.h"
14 #include "chrome/browser/download/download_service_factory.h"
15 #include "components/download/public/background_service/download_metadata.h"
16 #include "components/download/public/background_service/download_service.h"
17 #include "content/public/browser/background_fetch_response.h"
18 #include "content/public/browser/browser_context.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "services/network/public/cpp/resource_request_body.h"
21 #include "url/origin.h"
22 
23 namespace {
24 
25 using BackgroundFetchFailureReason =
26     content::BackgroundFetchResult::FailureReason;
ToBackgroundFetchFailureReason(download::Client::FailureReason reason)27 BackgroundFetchFailureReason ToBackgroundFetchFailureReason(
28     download::Client::FailureReason reason) {
29   switch (reason) {
30     case download::Client::FailureReason::NETWORK:
31       return BackgroundFetchFailureReason::NETWORK;
32     case download::Client::FailureReason::UPLOAD_TIMEDOUT:
33     case download::Client::FailureReason::TIMEDOUT:
34       return BackgroundFetchFailureReason::TIMEDOUT;
35     case download::Client::FailureReason::UNKNOWN:
36       return BackgroundFetchFailureReason::FETCH_ERROR;
37     case download::Client::FailureReason::ABORTED:
38     case download::Client::FailureReason::CANCELLED:
39       return BackgroundFetchFailureReason::CANCELLED;
40   }
41 }
42 
43 }  // namespace
44 
BackgroundFetchDownloadClient(content::BrowserContext * context)45 BackgroundFetchDownloadClient::BackgroundFetchDownloadClient(
46     content::BrowserContext* context)
47     : browser_context_(context), delegate_(nullptr) {}
48 
49 BackgroundFetchDownloadClient::~BackgroundFetchDownloadClient() = default;
50 
OnServiceInitialized(bool state_lost,const std::vector<download::DownloadMetaData> & downloads)51 void BackgroundFetchDownloadClient::OnServiceInitialized(
52     bool state_lost,
53     const std::vector<download::DownloadMetaData>& downloads) {
54   std::set<std::string> outstanding_guids =
55       GetDelegate()->TakeOutstandingGuids();
56   for (const auto& download : downloads) {
57     if (!outstanding_guids.count(download.guid)) {
58       // Background Fetch is not aware of this GUID, so it successfully
59       // completed but the information is still around.
60       continue;
61     }
62 
63     if (download.completion_info) {
64       // The download finished but was not persisted.
65       OnDownloadSucceeded(download.guid, *download.completion_info);
66       return;
67     }
68 
69     // The download is active, and will call the appropriate functions.
70 
71     if (download.paused) {
72       // We need to resurface the notification in a paused state.
73       content::BrowserThread::PostBestEffortTask(
74           FROM_HERE, base::SequencedTaskRunnerHandle::Get(),
75           base::BindOnce(&BackgroundFetchDelegateImpl::RestartPausedDownload,
76                          GetDelegate()->GetWeakPtr(), download.guid));
77     }
78   }
79 
80   // There is also the case that the Download Service is not aware of the GUID.
81   // i.e. there is a guid in |outstanding_guids| not in |downloads|.
82   // This can be due to:
83   // 1. The browser crashing before the download started.
84   // 2. The download failing before persisting the state.
85   // 3. The browser was forced to clean-up the the download.
86   // In either case the download should be allowed to restart, so there is
87   // nothing to do here.
88 }
89 
OnServiceUnavailable()90 void BackgroundFetchDownloadClient::OnServiceUnavailable() {}
91 
OnDownloadStarted(const std::string & guid,const std::vector<GURL> & url_chain,const scoped_refptr<const net::HttpResponseHeaders> & headers)92 void BackgroundFetchDownloadClient::OnDownloadStarted(
93     const std::string& guid,
94     const std::vector<GURL>& url_chain,
95     const scoped_refptr<const net::HttpResponseHeaders>& headers) {
96   // TODO(crbug.com/884672): Validate the chain/headers and cancel the download
97   // if invalid.
98   auto response =
99       std::make_unique<content::BackgroundFetchResponse>(url_chain, headers);
100   GetDelegate()->OnDownloadStarted(guid, std::move(response));
101 }
102 
OnDownloadUpdated(const std::string & guid,uint64_t bytes_uploaded,uint64_t bytes_downloaded)103 void BackgroundFetchDownloadClient::OnDownloadUpdated(
104     const std::string& guid,
105     uint64_t bytes_uploaded,
106     uint64_t bytes_downloaded) {
107   GetDelegate()->OnDownloadUpdated(guid, bytes_uploaded, bytes_downloaded);
108 }
109 
OnDownloadFailed(const std::string & guid,const download::CompletionInfo & info,download::Client::FailureReason reason)110 void BackgroundFetchDownloadClient::OnDownloadFailed(
111     const std::string& guid,
112     const download::CompletionInfo& info,
113     download::Client::FailureReason reason) {
114   auto response = std::make_unique<content::BackgroundFetchResponse>(
115       info.url_chain, info.response_headers);
116   auto result = std::make_unique<content::BackgroundFetchResult>(
117       std::move(response), base::Time::Now(),
118       ToBackgroundFetchFailureReason(reason));
119   GetDelegate()->OnDownloadFailed(guid, std::move(result));
120 }
121 
OnDownloadSucceeded(const std::string & guid,const download::CompletionInfo & info)122 void BackgroundFetchDownloadClient::OnDownloadSucceeded(
123     const std::string& guid,
124     const download::CompletionInfo& info) {
125   if (browser_context_->IsOffTheRecord())
126     DCHECK(info.blob_handle);
127   else
128     DCHECK(!info.path.empty());
129 
130   auto response = std::make_unique<content::BackgroundFetchResponse>(
131       info.url_chain, info.response_headers);
132   auto result = std::make_unique<content::BackgroundFetchResult>(
133       std::move(response), base::Time::Now(), info.path, info.blob_handle,
134       info.bytes_downloaded);
135 
136   GetDelegate()->OnDownloadSucceeded(guid, std::move(result));
137 }
138 
CanServiceRemoveDownloadedFile(const std::string & guid,bool force_delete)139 bool BackgroundFetchDownloadClient::CanServiceRemoveDownloadedFile(
140     const std::string& guid,
141     bool force_delete) {
142   // If |force_delete| is true the file will be removed anyway.
143   // TODO(rayankans): Add UMA to see how often this happens.
144   return force_delete || GetDelegate()->IsGuidOutstanding(guid);
145 }
146 
GetUploadData(const std::string & guid,download::GetUploadDataCallback callback)147 void BackgroundFetchDownloadClient::GetUploadData(
148     const std::string& guid,
149     download::GetUploadDataCallback callback) {
150   GetDelegate()->GetUploadData(guid, std::move(callback));
151 }
152 
GetDelegate()153 BackgroundFetchDelegateImpl* BackgroundFetchDownloadClient::GetDelegate() {
154   if (delegate_)
155     return delegate_.get();
156 
157   content::BackgroundFetchDelegate* delegate =
158       browser_context_->GetBackgroundFetchDelegate();
159 
160   delegate_ = static_cast<BackgroundFetchDelegateImpl*>(delegate)->GetWeakPtr();
161   DCHECK(delegate_);
162   return delegate_.get();
163 }
164