blob: 01a5c8aecd290d2c16b56797ff65ebdefe34ae96 [file] [log] [blame]
// Copyright 2020 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/feed/core/v2/image_fetcher.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/typed_macros.h"
#include "components/feed/core/v2/metrics_reporter.h"
#include "components/feed/core/v2/public/types.h"
#include "net/base/net_errors.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
namespace feed {
ImageFetcher::ImageFetcher(
scoped_refptr<::network::SharedURLLoaderFactory> url_loader_factory)
: url_loader_factory_(url_loader_factory) {}
ImageFetcher::~ImageFetcher() = default;
ImageFetchId ImageFetcher::Fetch(const GURL& url, ImageCallback callback) {
ImageFetchId id = id_generator_.GenerateNextId();
TRACE_EVENT_BEGIN("android.ui.jank", "FeedImage",
perfetto::Track(GetTrackId(id)), "url", url);
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("interest_feedv2_image_send", R"(
semantics {
sender: "Feed Library"
description: "Images for articles in the feed."
trigger: "Triggered when viewing the feed on the NTP."
data: "Request for an image containing an ID for the image and "
"device specs (e.g. screen size) for resizing images."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: NO
setting: "This can be disabled from the New Tab Page by collapsing "
"the articles section."
chrome_policy {
NTPContentSuggestionsEnabled {
policy_options {mode: MANDATORY}
NTPContentSuggestionsEnabled: false
}
}
})");
auto resource_request = std::make_unique<::network::ResourceRequest>();
resource_request->url = url;
resource_request->method = net::HttpRequestHeaders::kGetMethod;
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
auto simple_loader = network::SimpleURLLoader::Create(
std::move(resource_request), traffic_annotation);
auto* const simple_loader_ptr = simple_loader.get();
bool inserted =
pending_requests_
.try_emplace(id, std::move(simple_loader), std::move(callback))
.second;
DCHECK(inserted);
simple_loader_ptr->DownloadToString(
url_loader_factory_.get(),
base::BindOnce(&ImageFetcher::OnFetchComplete, weak_factory_.GetWeakPtr(),
id, url),
network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
return id;
}
void ImageFetcher::OnFetchComplete(ImageFetchId id,
const GURL& url,
std::unique_ptr<std::string> response_data) {
TRACE_EVENT_END("android.ui.jank", perfetto::Track(GetTrackId(id)), "bytes",
response_data ? response_data->size() : 0);
std::optional<PendingRequest> request = RemovePending(id);
if (!request)
return;
NetworkResponse response;
if (request->loader->ResponseInfo() &&
request->loader->ResponseInfo()->headers) {
response.status_code =
request->loader->ResponseInfo()->headers->response_code();
} else {
response.status_code = request->loader->NetError();
}
MetricsReporter::OnImageFetched(url, response.status_code);
if (response_data)
response.response_bytes = std::move(*response_data);
std::move(request->callback).Run(std::move(response));
}
void ImageFetcher::Cancel(ImageFetchId id) {
std::optional<PendingRequest> request = RemovePending(id);
if (!request)
return;
// Cancel the fetch before running the callback.
request->loader.reset();
std::move(request->callback)
.Run({/*response_bytes=*/std::string(), net::Error::ERR_ABORTED});
}
std::optional<ImageFetcher::PendingRequest> ImageFetcher::RemovePending(
ImageFetchId id) {
auto iterator = pending_requests_.find(id);
if (iterator == pending_requests_.end())
return std::nullopt;
auto request = std::make_optional(std::move(iterator->second));
pending_requests_.erase(iterator);
return request;
}
uint64_t ImageFetcher::GetTrackId(ImageFetchId id) const {
return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this)) +
id.GetUnsafeValue();
}
ImageFetcher::PendingRequest::PendingRequest(
std::unique_ptr<network::SimpleURLLoader> loader,
ImageCallback callback)
: loader(std::move(loader)), callback(std::move(callback)) {}
ImageFetcher::PendingRequest::PendingRequest(
ImageFetcher::PendingRequest&& other) = default;
ImageFetcher::PendingRequest& ImageFetcher::PendingRequest::operator=(
ImageFetcher::PendingRequest&& other) = default;
ImageFetcher::PendingRequest::~PendingRequest() = default;
} // namespace feed