blob: 5aa691cf406218c89da788391559ebd469c696db [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/image_fetcher/core/image_fetcher_impl.h"
#include <string>
#include "base/bind.h"
#include "base/threading/thread_task_runner_handle.h"
#include "net/base/load_flags.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "ui/gfx/image/image.h"
namespace image_fetcher {
ImageFetcherImpl::ImageFetcherImpl(
std::unique_ptr<ImageDecoder> image_decoder,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: url_loader_factory_(url_loader_factory),
image_decoder_(std::move(image_decoder)),
image_data_fetcher_(new ImageDataFetcher(url_loader_factory)),
weak_ptr_factory_(this) {}
ImageFetcherImpl::~ImageFetcherImpl() {}
ImageFetcherImpl::ImageRequest::ImageRequest() {}
ImageFetcherImpl::ImageRequest::ImageRequest(ImageRequest&& other) = default;
ImageFetcherImpl::ImageRequest::~ImageRequest() {}
void ImageFetcherImpl::FetchImageAndData(
const GURL& image_url,
ImageDataFetcherCallback image_data_callback,
ImageFetcherCallback image_callback,
ImageFetcherParams params) {
// Before starting to fetch the image. Look for a request in progress for
// |image_url|, and queue if appropriate.
auto it = pending_net_requests_.find(image_url);
if (it == pending_net_requests_.end()) {
ImageRequest request;
request.id = image_url.spec();
if (image_callback) {
request.image_callbacks.push_back(std::move(image_callback));
}
if (image_data_callback) {
request.image_data_callbacks.push_back(std::move(image_data_callback));
}
pending_net_requests_.emplace(image_url, std::move(request));
image_data_fetcher_->SetImageDownloadLimit(params.max_download_size());
image_data_fetcher_->FetchImageData(
image_url,
base::BindOnce(&ImageFetcherImpl::OnImageURLFetched,
base::Unretained(this), image_url, std::move(params)),
params.traffic_annotation());
} else {
ImageRequest* request = &it->second;
// Request in progress. Register as an interested callback.
// TODO(treib,markusheintz): We're not guaranteed that the ID also matches.
// We probably have to store them all.
if (image_callback) {
request->image_callbacks.push_back(std::move(image_callback));
}
// Call callback if data is already fetched, otherwise register it for
// later.
if (image_data_callback) {
if (!request->image_data.empty()) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(image_data_callback), request->image_data,
request->request_metadata));
} else {
request->image_data_callbacks.push_back(std::move(image_data_callback));
}
}
}
}
void ImageFetcherImpl::OnImageURLFetched(const GURL& image_url,
ImageFetcherParams params,
const std::string& image_data,
const RequestMetadata& metadata) {
auto it = pending_net_requests_.find(image_url);
DCHECK(it != pending_net_requests_.end());
ImageRequest* request = &it->second;
DCHECK(request->image_data.empty());
DCHECK_EQ(RequestMetadata::RESPONSE_CODE_INVALID,
request->request_metadata.http_response_code);
for (auto& callback : request->image_data_callbacks) {
std::move(callback).Run(image_data, metadata);
}
request->image_data_callbacks.clear();
if (image_data.empty() || request->image_callbacks.empty()) {
for (auto& callback : request->image_callbacks) {
std::move(callback).Run(gfx::Image(), metadata);
}
pending_net_requests_.erase(it);
return;
}
request->image_data = image_data;
request->request_metadata = metadata;
image_decoder_->DecodeImage(
image_data, params.frame_size(),
base::BindOnce(&ImageFetcherImpl::OnImageDecoded,
weak_ptr_factory_.GetWeakPtr(), image_url, metadata));
}
void ImageFetcherImpl::OnImageDecoded(const GURL& image_url,
const RequestMetadata& metadata,
const gfx::Image& image) {
// Get request for the given image_url from the request queue.
auto image_iter = pending_net_requests_.find(image_url);
DCHECK(image_iter != pending_net_requests_.end());
ImageRequest* request = &image_iter->second;
// Run all image callbacks.
for (auto& callback : request->image_callbacks) {
std::move(callback).Run(image, metadata);
}
// Erase the completed ImageRequest.
DCHECK(request->image_data_callbacks.empty());
pending_net_requests_.erase(image_iter);
}
ImageDecoder* ImageFetcherImpl::GetImageDecoder() {
return image_decoder_.get();
}
} // namespace image_fetcher