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