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