diff options
Diffstat (limited to 'chromium/components/image_fetcher')
21 files changed, 796 insertions, 305 deletions
diff --git a/chromium/components/image_fetcher/BUILD.gn b/chromium/components/image_fetcher/core/BUILD.gn index f911424ac1d..19d9cd4a7bd 100644 --- a/chromium/components/image_fetcher/BUILD.gn +++ b/chromium/components/image_fetcher/core/BUILD.gn @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -static_library("image_fetcher") { +static_library("core") { sources = [ "image_data_fetcher.cc", "image_data_fetcher.h", @@ -31,7 +31,7 @@ source_set("unit_tests") { "request_metadata_unittest.cc", ] deps = [ - ":image_fetcher", + ":core", "//net", "//net:test_support", "//testing/gmock", diff --git a/chromium/components/image_fetcher/image_data_fetcher.cc b/chromium/components/image_fetcher/core/image_data_fetcher.cc index 204fa95d7e8..3eac4508d21 100644 --- a/chromium/components/image_fetcher/image_data_fetcher.cc +++ b/chromium/components/image_fetcher/core/image_data_fetcher.cc @@ -2,19 +2,26 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/image_fetcher/image_data_fetcher.h" +#include "components/image_fetcher/core/image_data_fetcher.h" + +#include <utility> #include "net/base/load_flags.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" #include "net/url_request/url_fetcher.h" -#include "net/url_request/url_request.h" #include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_status.h" #include "url/gurl.h" using data_use_measurement::DataUseUserData; +namespace { + +const char kContentLocationHeader[] = "Content-Location"; + +} // namespace + namespace image_fetcher { // An active image URL fetcher request. The struct contains the related requests @@ -22,8 +29,7 @@ namespace image_fetcher { struct ImageDataFetcher::ImageDataFetcherRequest { ImageDataFetcherRequest(const ImageDataFetcherCallback& callback, std::unique_ptr<net::URLFetcher> url_fetcher) - : callback(callback), - url_fetcher(std::move(url_fetcher)) {} + : callback(callback), url_fetcher(std::move(url_fetcher)) {} ~ImageDataFetcherRequest() {} @@ -47,6 +53,11 @@ void ImageDataFetcher::SetDataUseServiceName( data_use_service_name_ = data_use_service_name; } +void ImageDataFetcher::SetImageDownloadLimit( + base::Optional<int64_t> max_download_bytes) { + max_download_bytes_ = max_download_bytes; +} + void ImageDataFetcher::FetchImageData( const GURL& image_url, const ImageDataFetcherCallback& callback) { @@ -70,32 +81,63 @@ void ImageDataFetcher::FetchImageData( request->url_fetcher->SetRequestContext(url_request_context_getter_.get()); request->url_fetcher->SetReferrer(referrer); request->url_fetcher->SetReferrerPolicy(referrer_policy); + request->url_fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | + net::LOAD_DO_NOT_SAVE_COOKIES | + net::LOAD_DO_NOT_SEND_AUTH_DATA); request->url_fetcher->Start(); pending_requests_[request->url_fetcher.get()] = std::move(request); } void ImageDataFetcher::OnURLFetchComplete(const net::URLFetcher* source) { - auto request_iter = pending_requests_.find(source); - DCHECK(request_iter != pending_requests_.end()); - + DCHECK(pending_requests_.find(source) != pending_requests_.end()); bool success = source->GetStatus().status() == net::URLRequestStatus::SUCCESS; RequestMetadata metadata; - metadata.response_code = RESPONSE_CODE_INVALID; if (success && source->GetResponseHeaders()) { source->GetResponseHeaders()->GetMimeType(&metadata.mime_type); - metadata.response_code = source->GetResponseHeaders()->response_code(); - success &= (metadata.response_code == net::HTTP_OK); + metadata.http_response_code = source->GetResponseHeaders()->response_code(); + // Just read the first value-pair for this header (not caring about |iter|). + source->GetResponseHeaders()->EnumerateHeader( + /*iter=*/nullptr, kContentLocationHeader, + &metadata.content_location_header); + success &= (metadata.http_response_code == net::HTTP_OK); } + metadata.from_http_cache = source->WasCached(); std::string image_data; if (success) { source->GetResponseAsString(&image_data); } - request_iter->second->callback.Run(image_data, metadata); + FinishRequest(source, metadata, image_data); +} + +void ImageDataFetcher::OnURLFetchDownloadProgress( + const net::URLFetcher* source, + int64_t current, + int64_t total, + int64_t current_network_bytes) { + if (!max_download_bytes_.has_value()) { + return; + } + if (total <= max_download_bytes_.value() && + current <= max_download_bytes_.value()) { + return; + } + DCHECK(pending_requests_.find(source) != pending_requests_.end()); + DLOG(WARNING) << "Image data exceeded download size limit."; + RequestMetadata metadata; + metadata.http_response_code = net::URLFetcher::RESPONSE_CODE_INVALID; - // Remove the finished request. + FinishRequest(source, metadata, /*image_data=*/std::string()); +} + +void ImageDataFetcher::FinishRequest(const net::URLFetcher* source, + const RequestMetadata& metadata, + const std::string& image_data) { + auto request_iter = pending_requests_.find(source); + DCHECK(request_iter != pending_requests_.end()); + request_iter->second->callback.Run(image_data, metadata); pending_requests_.erase(request_iter); } diff --git a/chromium/components/image_fetcher/image_data_fetcher.h b/chromium/components/image_fetcher/core/image_data_fetcher.h index d05ec3d4bb5..169ad54d2ce 100644 --- a/chromium/components/image_fetcher/image_data_fetcher.h +++ b/chromium/components/image_fetcher/core/image_data_fetcher.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_IMAGE_FETCHER_IMAGE_DATA_FETCHER_H_ -#define COMPONENTS_IMAGE_FETCHER_IMAGE_DATA_FETCHER_H_ +#ifndef COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_DATA_FETCHER_H_ +#define COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_DATA_FETCHER_H_ #include <map> #include <memory> @@ -12,9 +12,9 @@ #include "base/callback.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/optional.h" #include "components/data_use_measurement/core/data_use_user_data.h" -#include "components/image_fetcher/request_metadata.h" -#include "net/url_request/url_fetcher.h" +#include "components/image_fetcher/core/request_metadata.h" #include "net/url_request/url_fetcher_delegate.h" #include "net/url_request/url_request.h" #include "url/gurl.h" @@ -28,14 +28,10 @@ namespace image_fetcher { class ImageDataFetcher : public net::URLFetcherDelegate { public: - // Impossible http response code. Used to signal that no http response code - // was received. - enum ResponseCode { - RESPONSE_CODE_INVALID = net::URLFetcher::RESPONSE_CODE_INVALID - }; - // Callback with the |image_data|. If an error prevented a http response, // |request_metadata.response_code| will be RESPONSE_CODE_INVALID. + // TODO(treib): Pass |image_data| out by value, or use RefCountedBytes, to + // avoid copying. using ImageDataFetcherCallback = base::Callback<void(const std::string& image_data, const RequestMetadata& request_metadata)>; @@ -49,6 +45,10 @@ class ImageDataFetcher : public net::URLFetcherDelegate { // Sets a service name against which to track data usage. void SetDataUseServiceName(DataUseServiceName data_use_service_name); + // Sets an upper limit for image downloads. + // Already running downloads are affected. + void SetImageDownloadLimit(base::Optional<int64_t> max_download_bytes); + // Fetches the raw image bytes from the given |image_url| and calls the given // |callback|. The callback is run even if fetching the URL fails. In case // of an error an empty string is passed to the callback. @@ -64,8 +64,16 @@ class ImageDataFetcher : public net::URLFetcherDelegate { private: struct ImageDataFetcherRequest; - // Method inherited from URLFetcherDelegate + // Methods inherited from URLFetcherDelegate void OnURLFetchComplete(const net::URLFetcher* source) override; + void OnURLFetchDownloadProgress(const net::URLFetcher* source, + int64_t current, + int64_t total, + int64_t current_network_bytes) override; + + void FinishRequest(const net::URLFetcher* source, + const RequestMetadata& metadata, + const std::string& image_data); // All active image url requests. std::map<const net::URLFetcher*, std::unique_ptr<ImageDataFetcherRequest>> @@ -82,9 +90,12 @@ class ImageDataFetcher : public net::URLFetcherDelegate { // is not used. int next_url_fetcher_id_; + // Upper limit for the number of bytes to download per image. + base::Optional<int64_t> max_download_bytes_; + DISALLOW_COPY_AND_ASSIGN(ImageDataFetcher); }; } // namespace image_fetcher -#endif // COMPONENTS_IMAGE_FETCHER_IMAGE_DATA_FETCHER_H_ +#endif // COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_DATA_FETCHER_H_ diff --git a/chromium/components/image_fetcher/core/image_data_fetcher_unittest.cc b/chromium/components/image_fetcher/core/image_data_fetcher_unittest.cc new file mode 100644 index 00000000000..de47936940e --- /dev/null +++ b/chromium/components/image_fetcher/core/image_data_fetcher_unittest.cc @@ -0,0 +1,297 @@ +// Copyright 2016 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_data_fetcher.h" + +#include <memory> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop.h" +#include "net/base/load_flags.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_status_code.h" +#include "net/url_request/test_url_fetcher_factory.h" +#include "net/url_request/url_request_status.h" +#include "net/url_request/url_request_test_util.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const char kImageURL[] = "http://www.example.com/image"; +const char kURLResponseData[] = "EncodedImageData"; + +} // namespace + +namespace image_fetcher { + +class ImageDataFetcherTest : public testing::Test { + public: + ImageDataFetcherTest() + : test_request_context_getter_( + new net::TestURLRequestContextGetter(message_loop_.task_runner())), + image_data_fetcher_(test_request_context_getter_.get()) {} + ~ImageDataFetcherTest() override {} + + MOCK_METHOD2(OnImageDataFetched, + void(const std::string&, const RequestMetadata&)); + + MOCK_METHOD2(OnImageDataFetchedFailedRequest, + void(const std::string&, const RequestMetadata&)); + + MOCK_METHOD2(OnImageDataFetchedMultipleRequests, + void(const std::string&, const RequestMetadata&)); + + protected: + base::MessageLoop message_loop_; + + scoped_refptr<net::URLRequestContextGetter> test_request_context_getter_; + + ImageDataFetcher image_data_fetcher_; + + net::TestURLFetcherFactory fetcher_factory_; + + private: + DISALLOW_COPY_AND_ASSIGN(ImageDataFetcherTest); +}; + +TEST_F(ImageDataFetcherTest, FetchImageData) { + image_data_fetcher_.FetchImageData( + GURL(kImageURL), base::Bind(&ImageDataFetcherTest::OnImageDataFetched, + base::Unretained(this))); + + RequestMetadata expected_metadata; + expected_metadata.mime_type = std::string("image/png"); + expected_metadata.http_response_code = net::HTTP_OK; + EXPECT_CALL(*this, OnImageDataFetched(std::string(kURLResponseData), + expected_metadata)); + + // Get and configure the TestURLFetcher. + net::TestURLFetcher* test_url_fetcher = fetcher_factory_.GetFetcherByID(0); + ASSERT_NE(nullptr, test_url_fetcher); + EXPECT_TRUE(test_url_fetcher->GetLoadFlags() & net::LOAD_DO_NOT_SEND_COOKIES); + EXPECT_TRUE(test_url_fetcher->GetLoadFlags() & net::LOAD_DO_NOT_SAVE_COOKIES); + EXPECT_TRUE(test_url_fetcher->GetLoadFlags() & + net::LOAD_DO_NOT_SEND_AUTH_DATA); + test_url_fetcher->set_status( + net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK)); + test_url_fetcher->SetResponseString(kURLResponseData); + test_url_fetcher->set_response_code(net::HTTP_OK); + + std::string raw_header = + "HTTP/1.1 200 OK\n" + "Content-type: image/png\n\n"; + std::replace(raw_header.begin(), raw_header.end(), '\n', '\0'); + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders(raw_header)); + test_url_fetcher->set_response_headers(headers); + + // Call the URLFetcher delegate to continue the test. + test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher); +} + +TEST_F(ImageDataFetcherTest, FetchImageData_FromCache) { + image_data_fetcher_.FetchImageData( + GURL(kImageURL), base::Bind(&ImageDataFetcherTest::OnImageDataFetched, + base::Unretained(this))); + + RequestMetadata expected_metadata; + expected_metadata.mime_type = std::string("image/png"); + expected_metadata.http_response_code = net::HTTP_OK; + expected_metadata.from_http_cache = true; + EXPECT_CALL(*this, OnImageDataFetched(std::string(kURLResponseData), + expected_metadata)); + + // Get and configure the TestURLFetcher. + net::TestURLFetcher* test_url_fetcher = fetcher_factory_.GetFetcherByID(0); + ASSERT_NE(nullptr, test_url_fetcher); + test_url_fetcher->set_status( + net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK)); + test_url_fetcher->SetResponseString(kURLResponseData); + test_url_fetcher->set_response_code(net::HTTP_OK); + test_url_fetcher->set_was_cached(true); + + std::string raw_header = + "HTTP/1.1 200 OK\n" + "Content-type: image/png\n\n"; + std::replace(raw_header.begin(), raw_header.end(), '\n', '\0'); + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders(raw_header)); + test_url_fetcher->set_response_headers(headers); + + // Call the URLFetcher delegate to continue the test. + test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher); +} + +TEST_F(ImageDataFetcherTest, FetchImageData_NotFound) { + image_data_fetcher_.FetchImageData( + GURL(kImageURL), base::Bind(&ImageDataFetcherTest::OnImageDataFetched, + base::Unretained(this))); + + RequestMetadata expected_metadata; + expected_metadata.mime_type = std::string("image/png"); + expected_metadata.http_response_code = net::HTTP_NOT_FOUND; + // For 404, expect an empty result even though correct image data is sent. + EXPECT_CALL(*this, OnImageDataFetched(std::string(), expected_metadata)); + + // Get and configure the TestURLFetcher. + net::TestURLFetcher* test_url_fetcher = fetcher_factory_.GetFetcherByID(0); + ASSERT_NE(nullptr, test_url_fetcher); + test_url_fetcher->set_status( + net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK)); + test_url_fetcher->SetResponseString(kURLResponseData); + + std::string raw_header = + "HTTP/1.1 404 Not Found\n" + "Content-type: image/png\n\n"; + std::replace(raw_header.begin(), raw_header.end(), '\n', '\0'); + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders(raw_header)); + test_url_fetcher->set_response_headers(headers); + + // Call the URLFetcher delegate to continue the test. + test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher); +} + +TEST_F(ImageDataFetcherTest, FetchImageData_WithContentLocation) { + image_data_fetcher_.FetchImageData( + GURL(kImageURL), base::Bind(&ImageDataFetcherTest::OnImageDataFetched, + base::Unretained(this))); + + RequestMetadata expected_metadata; + expected_metadata.mime_type = std::string("image/png"); + expected_metadata.http_response_code = net::HTTP_NOT_FOUND; + expected_metadata.content_location_header = "http://test-location/image.png"; + // For 404, expect an empty result even though correct image data is sent. + EXPECT_CALL(*this, OnImageDataFetched(std::string(), expected_metadata)); + + // Get and configure the TestURLFetcher. + net::TestURLFetcher* test_url_fetcher = fetcher_factory_.GetFetcherByID(0); + ASSERT_NE(nullptr, test_url_fetcher); + test_url_fetcher->set_status( + net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK)); + test_url_fetcher->SetResponseString(kURLResponseData); + + std::string raw_header = + "HTTP/1.1 404 Not Found\n" + "Content-type: image/png\n" + "Content-location: http://test-location/image.png\n\n"; + std::replace(raw_header.begin(), raw_header.end(), '\n', '\0'); + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders(raw_header)); + test_url_fetcher->set_response_headers(headers); + + // Call the URLFetcher delegate to continue the test. + test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher); +} + +TEST_F(ImageDataFetcherTest, FetchImageData_FailedRequest) { + image_data_fetcher_.FetchImageData( + GURL(kImageURL), + base::Bind(&ImageDataFetcherTest::OnImageDataFetchedFailedRequest, + base::Unretained(this))); + + RequestMetadata expected_metadata; + expected_metadata.http_response_code = net::URLFetcher::RESPONSE_CODE_INVALID; + EXPECT_CALL( + *this, OnImageDataFetchedFailedRequest(std::string(), expected_metadata)); + + // Get and configure the TestURLFetcher. + net::TestURLFetcher* test_url_fetcher = fetcher_factory_.GetFetcherByID(0); + ASSERT_NE(nullptr, test_url_fetcher); + test_url_fetcher->set_status(net::URLRequestStatus( + net::URLRequestStatus::FAILED, net::ERR_INVALID_URL)); + + // Call the URLFetcher delegate to continue the test. + test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher); +} + +TEST_F(ImageDataFetcherTest, FetchImageData_MultipleRequests) { + ImageDataFetcher::ImageDataFetcherCallback callback = + base::Bind(&ImageDataFetcherTest::OnImageDataFetchedMultipleRequests, + base::Unretained(this)); + EXPECT_CALL(*this, OnImageDataFetchedMultipleRequests(testing::_, testing::_)) + .Times(2); + + image_data_fetcher_.FetchImageData(GURL(kImageURL), callback); + image_data_fetcher_.FetchImageData(GURL(kImageURL), callback); + + // Multiple calls to FetchImageData for the same URL will result in + // multiple URLFetchers being created. + net::TestURLFetcher* test_url_fetcher = fetcher_factory_.GetFetcherByID(0); + ASSERT_NE(nullptr, test_url_fetcher); + test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher); + + test_url_fetcher = fetcher_factory_.GetFetcherByID(1); + ASSERT_NE(nullptr, test_url_fetcher); + test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher); +} + +TEST_F(ImageDataFetcherTest, FetchImageData_CancelFetchIfImageExceedsMaxSize) { + // In order to know whether the fetcher was canceled, it must notify about its + // deletion. + fetcher_factory_.set_remove_fetcher_on_delete(true); + + const int64_t kMaxDownloadBytes = 1024 * 1024; + image_data_fetcher_.SetImageDownloadLimit(kMaxDownloadBytes); + image_data_fetcher_.FetchImageData( + GURL(kImageURL), base::Bind(&ImageDataFetcherTest::OnImageDataFetched, + base::Unretained(this))); + + // Fetching an oversized image will behave like any other failed request. + // There will be exactly one call to OnImageDataFetched containing a response + // code that would be impossible for a completed fetch. + RequestMetadata expected_metadata; + expected_metadata.http_response_code = net::URLFetcher::RESPONSE_CODE_INVALID; + EXPECT_CALL(*this, OnImageDataFetched(std::string(), expected_metadata)); + + // Get and configure the TestURLFetcher. + net::TestURLFetcher* test_url_fetcher = fetcher_factory_.GetFetcherByID(0); + ASSERT_NE(nullptr, test_url_fetcher); + + // Create a completely valid response that is never used. This is to make sure + // that the answer isn't accidentally invalid but intentionally. + test_url_fetcher->set_status( + net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK)); + test_url_fetcher->SetResponseString(kURLResponseData); + test_url_fetcher->set_response_code(net::HTTP_OK); + std::string raw_header = + "HTTP/1.1 200 OK\n" + "Content-type: image/png\n\n"; + std::replace(raw_header.begin(), raw_header.end(), '\n', '\0'); + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders(raw_header)); + test_url_fetcher->set_response_headers(headers); + + test_url_fetcher->delegate()->OnURLFetchDownloadProgress( + test_url_fetcher, + /*current=*/0, // Bytes received up to the call. + /*total=*/-1, // not determined + /*current_network_bytes=*/0); // not relevant + // The URL fetch should be running ... + ASSERT_NE(nullptr, fetcher_factory_.GetFetcherByID(0)); + + test_url_fetcher->delegate()->OnURLFetchDownloadProgress( + test_url_fetcher, + 768 * 1024, // Current bytes are not exeeding the limit. + /*total=*/-1, /*current_network_bytes=*/0); + // ... and running ... + ASSERT_NE(nullptr, fetcher_factory_.GetFetcherByID(0)); + + test_url_fetcher->delegate()->OnURLFetchDownloadProgress( + test_url_fetcher, kMaxDownloadBytes, // Still not exeeding the limit. + /*total=*/-1, /*current_network_bytes=*/0); + // ... and running ... + ASSERT_NE(nullptr, fetcher_factory_.GetFetcherByID(0)); + + test_url_fetcher->delegate()->OnURLFetchDownloadProgress( + test_url_fetcher, kMaxDownloadBytes + 1, // Limits are exceeded. + /*total=*/-1, /*current_network_bytes=*/0); + // ... and be canceled. + EXPECT_EQ(nullptr, fetcher_factory_.GetFetcherByID(0)); +} + +} // namespace image_fetcher diff --git a/chromium/components/image_fetcher/image_decoder.h b/chromium/components/image_fetcher/core/image_decoder.h index b5589f17b4a..5f03db12d4c 100644 --- a/chromium/components/image_fetcher/image_decoder.h +++ b/chromium/components/image_fetcher/core/image_decoder.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_IMAGE_FETCHER_IMAGE_DECODER_H_ -#define COMPONENTS_IMAGE_FETCHER_IMAGE_DECODER_H_ +#ifndef COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_DECODER_H_ +#define COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_DECODER_H_ #include <string> @@ -43,4 +43,4 @@ class ImageDecoder { } // namespace image_fetcher -#endif // COMPONENTS_IMAGE_FETCHER_IMAGE_DECODER_H_ +#endif // COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_DECODER_H_ diff --git a/chromium/components/image_fetcher/image_fetcher.h b/chromium/components/image_fetcher/core/image_fetcher.h index 3626c3af704..0284aee9f25 100644 --- a/chromium/components/image_fetcher/image_fetcher.h +++ b/chromium/components/image_fetcher/core/image_fetcher.h @@ -2,15 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_IMAGE_FETCHER_IMAGE_FETCHER_H_ -#define COMPONENTS_IMAGE_FETCHER_IMAGE_FETCHER_H_ +#ifndef COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_FETCHER_H_ +#define COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_FETCHER_H_ #include <string> #include "base/callback.h" #include "base/macros.h" +#include "base/optional.h" #include "components/data_use_measurement/core/data_use_user_data.h" -#include "components/image_fetcher/image_fetcher_delegate.h" +#include "components/image_fetcher/core/image_fetcher_delegate.h" #include "url/gurl.h" namespace gfx { @@ -20,6 +21,10 @@ class Size; namespace image_fetcher { +class ImageDecoder; + +struct RequestMetadata; + // A class used to fetch server images. It can be called from any thread and the // callback will be called on the thread which initiated the fetch. class ImageFetcher { @@ -27,6 +32,11 @@ class ImageFetcher { ImageFetcher() {} virtual ~ImageFetcher() {} + using ImageFetcherCallback = + base::Callback<void(const std::string& id, + const gfx::Image& image, + const RequestMetadata& metadata)>; + using DataUseServiceName = data_use_measurement::DataUseUserData::ServiceName; virtual void SetImageFetcherDelegate(ImageFetcherDelegate* delegate) = 0; @@ -35,6 +45,12 @@ class ImageFetcher { virtual void SetDataUseServiceName( DataUseServiceName data_use_service_name) = 0; + // Sets an upper limit for image downloads that is by default disabled. + // Setting |max_download_bytes| to a negative value will disable the limit. + // Already running downloads are immediately affected. + virtual void SetImageDownloadLimit( + base::Optional<int64_t> max_download_bytes) = 0; + // Sets the desired size for images with multiple frames (like .ico files). // By default, the image fetcher choses smaller images. Override to choose a // frame with a size as close as possible to |size| (trying to take one in @@ -48,10 +64,9 @@ class ImageFetcher { virtual void StartOrQueueNetworkRequest( const std::string& id, const GURL& image_url, - base::Callback<void(const std::string&, const gfx::Image&)> callback) = 0; + const ImageFetcherCallback& callback) = 0; - // TODO(treib,markusheintz): Now that iOS uses the same ImageFetcherImpl (see - // crbug.com/689020), add a getter for the ImageDecoder here. + virtual ImageDecoder* GetImageDecoder() = 0; private: DISALLOW_COPY_AND_ASSIGN(ImageFetcher); @@ -59,4 +74,4 @@ class ImageFetcher { } // namespace image_fetcher -#endif // COMPONENTS_IMAGE_FETCHER_IMAGE_FETCHER_H_ +#endif // COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_FETCHER_H_ diff --git a/chromium/components/image_fetcher/image_fetcher_delegate.h b/chromium/components/image_fetcher/core/image_fetcher_delegate.h index 38d66b29543..fd9c362fcb9 100644 --- a/chromium/components/image_fetcher/image_fetcher_delegate.h +++ b/chromium/components/image_fetcher/core/image_fetcher_delegate.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_IMAGE_FETCHER_IMAGE_FETCHER_DELEGATE_H_ -#define COMPONENTS_IMAGE_FETCHER_IMAGE_FETCHER_DELEGATE_H_ +#ifndef COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_FETCHER_DELEGATE_H_ +#define COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_FETCHER_DELEGATE_H_ #include <string> @@ -24,13 +24,12 @@ class ImageFetcherDelegate { // stores (generally compressed) image data owned by the caller, and can be // empty if the fetch failed. virtual void OnImageDataFetched(const std::string& id, - const std::string& data) {}; + const std::string& data) {} // Called when an image was fetched and decoded. |id| is an identifier for the // fetch (as passed to ImageFetcher::StartOrQueueNetworkRequest); |image| // stores image data owned by the caller, and can be an empty gfx::Image. - virtual void OnImageFetched(const std::string& id, - const gfx::Image& image) {}; + virtual void OnImageFetched(const std::string& id, const gfx::Image& image) {} protected: virtual ~ImageFetcherDelegate() {} @@ -40,4 +39,4 @@ class ImageFetcherDelegate { } // namespace image_fetcher -#endif // COMPONENTS_IMAGE_FETCHER_IMAGE_FETCHER_DELEGATE_H_ +#endif // COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_FETCHER_DELEGATE_H_ diff --git a/chromium/components/image_fetcher/image_fetcher_impl.cc b/chromium/components/image_fetcher/core/image_fetcher_impl.cc index f65155c65e7..567928c9a6d 100644 --- a/chromium/components/image_fetcher/image_fetcher_impl.cc +++ b/chromium/components/image_fetcher/core/image_fetcher_impl.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/image_fetcher/image_fetcher_impl.h" +#include "components/image_fetcher/core/image_fetcher_impl.h" #include <string> @@ -15,10 +15,10 @@ namespace image_fetcher { ImageFetcherImpl::ImageFetcherImpl( std::unique_ptr<ImageDecoder> image_decoder, net::URLRequestContextGetter* url_request_context) - : delegate_(nullptr), url_request_context_(url_request_context), + : delegate_(nullptr), + url_request_context_(url_request_context), image_decoder_(std::move(image_decoder)), - image_data_fetcher_(new ImageDataFetcher(url_request_context_.get())) { -} + image_data_fetcher_(new ImageDataFetcher(url_request_context_.get())) {} ImageFetcherImpl::~ImageFetcherImpl() {} @@ -27,7 +27,7 @@ ImageFetcherImpl::ImageRequest::ImageRequest() {} ImageFetcherImpl::ImageRequest::ImageRequest(const ImageRequest& other) = default; -ImageFetcherImpl::ImageRequest::~ImageRequest() { } +ImageFetcherImpl::ImageRequest::~ImageRequest() {} void ImageFetcherImpl::SetImageFetcherDelegate(ImageFetcherDelegate* delegate) { DCHECK(delegate); @@ -43,10 +43,15 @@ void ImageFetcherImpl::SetDesiredImageFrameSize(const gfx::Size& size) { desired_image_frame_size_ = size; } +void ImageFetcherImpl::SetImageDownloadLimit( + base::Optional<int64_t> max_download_bytes) { + image_data_fetcher_->SetImageDownloadLimit(max_download_bytes); +} + void ImageFetcherImpl::StartOrQueueNetworkRequest( const std::string& id, const GURL& image_url, - base::Callback<void(const std::string&, const gfx::Image&)> callback) { + const ImageFetcherCallback& callback) { // Before starting to fetch the image. Look for a request in progress for // |image_url|, and queue if appropriate. ImageRequestMap::iterator it = pending_net_requests_.find(image_url); @@ -57,9 +62,8 @@ void ImageFetcherImpl::StartOrQueueNetworkRequest( pending_net_requests_[image_url].swap(&request); image_data_fetcher_->FetchImageData( - image_url, - base::Bind(&ImageFetcherImpl::OnImageURLFetched, - base::Unretained(this), image_url)); + image_url, base::Bind(&ImageFetcherImpl::OnImageURLFetched, + base::Unretained(this), image_url)); } else { // Request in progress. Register as an interested callback. it->second.callbacks.push_back(callback); @@ -76,12 +80,14 @@ void ImageFetcherImpl::OnImageURLFetched(const GURL& image_url, delegate_->OnImageDataFetched(it->second.id, image_data); } - image_decoder_->DecodeImage(image_data, desired_image_frame_size_, - base::Bind(&ImageFetcherImpl::OnImageDecoded, - base::Unretained(this), image_url)); + image_decoder_->DecodeImage( + image_data, desired_image_frame_size_, + base::Bind(&ImageFetcherImpl::OnImageDecoded, base::Unretained(this), + 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. ImageRequestMap::iterator image_iter = pending_net_requests_.find(image_url); @@ -90,7 +96,7 @@ void ImageFetcherImpl::OnImageDecoded(const GURL& image_url, // Run all callbacks for (const auto& callback : request->callbacks) { - callback.Run(request->id, image); + callback.Run(request->id, image, metadata); } // Inform the ImageFetcherDelegate. @@ -102,4 +108,8 @@ void ImageFetcherImpl::OnImageDecoded(const GURL& image_url, pending_net_requests_.erase(image_iter); } +ImageDecoder* ImageFetcherImpl::GetImageDecoder() { + return image_decoder_.get(); +} + } // namespace image_fetcher diff --git a/chromium/components/image_fetcher/image_fetcher_impl.h b/chromium/components/image_fetcher/core/image_fetcher_impl.h index 16b348f854e..697217c7f79 100644 --- a/chromium/components/image_fetcher/image_fetcher_impl.h +++ b/chromium/components/image_fetcher/core/image_fetcher_impl.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_IMAGE_FETCHER_IMAGE_FETCHER_IMPL_H_ -#define COMPONENTS_IMAGE_FETCHER_IMAGE_FETCHER_IMPL_H_ +#ifndef COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_FETCHER_IMPL_H_ +#define COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_FETCHER_IMPL_H_ #include <map> #include <memory> @@ -13,9 +13,9 @@ #include "base/callback.h" #include "base/macros.h" -#include "components/image_fetcher/image_data_fetcher.h" -#include "components/image_fetcher/image_decoder.h" -#include "components/image_fetcher/image_fetcher.h" +#include "components/image_fetcher/core/image_data_fetcher.h" +#include "components/image_fetcher/core/image_decoder.h" +#include "components/image_fetcher/core/image_fetcher.h" #include "ui/gfx/geometry/size.h" #include "url/gurl.h" @@ -32,9 +32,8 @@ namespace image_fetcher { // The standard (non-test) implementation of ImageFetcher. class ImageFetcherImpl : public image_fetcher::ImageFetcher { public: - ImageFetcherImpl( - std::unique_ptr<ImageDecoder> image_decoder, - net::URLRequestContextGetter* url_request_context); + ImageFetcherImpl(std::unique_ptr<ImageDecoder> image_decoder, + net::URLRequestContextGetter* url_request_context); ~ImageFetcherImpl() override; // Sets the |delegate| of the ImageFetcherImpl. The |delegate| has to be alive @@ -47,16 +46,17 @@ class ImageFetcherImpl : public image_fetcher::ImageFetcher { void SetDesiredImageFrameSize(const gfx::Size& size) override; + void SetImageDownloadLimit( + base::Optional<int64_t> max_download_bytes) override; + void StartOrQueueNetworkRequest( const std::string& id, const GURL& image_url, - base::Callback<void(const std::string&, const gfx::Image&)> callback) - override; + const ImageFetcherCallback& callback) override; - private: - using CallbackVector = - std::vector<base::Callback<void(const std::string&, const gfx::Image&)>>; + ImageDecoder* GetImageDecoder() override; + private: // State related to an image fetch (id, pending callbacks). struct ImageRequest { ImageRequest(); @@ -71,7 +71,7 @@ class ImageFetcherImpl : public image_fetcher::ImageFetcher { std::string id; // Queue for pending callbacks, which may accumulate while the request is in // flight. - CallbackVector callbacks; + std::vector<ImageFetcherCallback> callbacks; }; using ImageRequestMap = std::map<const GURL, ImageRequest>; @@ -84,7 +84,9 @@ class ImageFetcherImpl : public image_fetcher::ImageFetcher { // Processes image decoded events. This is the continuation method used for // creating callbacks that are passed to the ImageDecoder. - void OnImageDecoded(const GURL& image_url, const gfx::Image& image); + void OnImageDecoded(const GURL& image_url, + const RequestMetadata& metadata, + const gfx::Image& image); ImageFetcherDelegate* delegate_; @@ -105,4 +107,4 @@ class ImageFetcherImpl : public image_fetcher::ImageFetcher { } // namespace image_fetcher -#endif // COMPONENTS_IMAGE_FETCHER_IMAGE_FETCHER_IMPL_H_ +#endif // COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_FETCHER_IMPL_H_ diff --git a/chromium/components/image_fetcher/request_metadata.cc b/chromium/components/image_fetcher/core/request_metadata.cc index 172dc5c2ab1..13ffadaa357 100644 --- a/chromium/components/image_fetcher/request_metadata.cc +++ b/chromium/components/image_fetcher/core/request_metadata.cc @@ -2,13 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/image_fetcher/request_metadata.h" +#include "components/image_fetcher/core/request_metadata.h" namespace image_fetcher { +RequestMetadata::RequestMetadata() + : http_response_code(RESPONSE_CODE_INVALID), from_http_cache(false) {} + bool operator==(const RequestMetadata& lhs, const RequestMetadata& rhs) { return lhs.mime_type == rhs.mime_type && - lhs.response_code == rhs.response_code; + lhs.http_response_code == rhs.http_response_code && + lhs.from_http_cache == rhs.from_http_cache && + lhs.content_location_header == rhs.content_location_header; } bool operator!=(const RequestMetadata& lhs, const RequestMetadata& rhs) { diff --git a/chromium/components/image_fetcher/core/request_metadata.h b/chromium/components/image_fetcher/core/request_metadata.h new file mode 100644 index 00000000000..630ffc6956c --- /dev/null +++ b/chromium/components/image_fetcher/core/request_metadata.h @@ -0,0 +1,35 @@ +// Copyright 2017 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. + +#ifndef COMPONENTS_IMAGE_FETCHER_CORE_REQUEST_METADATA_H_ +#define COMPONENTS_IMAGE_FETCHER_CORE_REQUEST_METADATA_H_ + +#include <string> + +#include "net/url_request/url_fetcher.h" + +namespace image_fetcher { + +// Metadata for a URL request. +struct RequestMetadata { + // Impossible http response code. Used to signal that no http response code + // was received. + enum ResponseCode { + RESPONSE_CODE_INVALID = net::URLFetcher::RESPONSE_CODE_INVALID + }; + + RequestMetadata(); + + std::string mime_type; + int http_response_code; + bool from_http_cache; + std::string content_location_header; +}; + +bool operator==(const RequestMetadata& lhs, const RequestMetadata& rhs); +bool operator!=(const RequestMetadata& lhs, const RequestMetadata& rhs); + +} // namespace image_fetcher + +#endif // COMPONENTS_IMAGE_FETCHER_CORE_REQUEST_METADATA_H_ diff --git a/chromium/components/image_fetcher/core/request_metadata_unittest.cc b/chromium/components/image_fetcher/core/request_metadata_unittest.cc new file mode 100644 index 00000000000..ea0d00f4e5c --- /dev/null +++ b/chromium/components/image_fetcher/core/request_metadata_unittest.cc @@ -0,0 +1,57 @@ +// Copyright 2017 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/request_metadata.h" + +#include "base/memory/ref_counted.h" +#include "net/http/http_response_headers.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace image_fetcher { + +TEST(RequestMetadataTest, Equality) { + RequestMetadata rhs; + RequestMetadata lhs; + rhs.mime_type = "testMimeType"; + lhs.mime_type = "testMimeType"; + rhs.http_response_code = 1; + lhs.http_response_code = 1; + rhs.from_http_cache = true; + lhs.from_http_cache = true; + lhs.content_location_header = "http://test-location.com/image.png"; + rhs.content_location_header = "http://test-location.com/image.png"; + + EXPECT_EQ(rhs, lhs); +} + +TEST(RequestMetadataTest, NoEquality) { + RequestMetadata rhs; + RequestMetadata lhs; + rhs.mime_type = "testMimeType"; + lhs.mime_type = "testMimeType"; + rhs.http_response_code = 1; + lhs.http_response_code = 1; + rhs.from_http_cache = true; + lhs.from_http_cache = true; + lhs.content_location_header = "http://test-location.com/image.png"; + rhs.content_location_header = "http://test-location.com/image.png"; + + lhs.mime_type = "testOtherMimeType"; + EXPECT_NE(rhs, lhs); + lhs.mime_type = "testMimeType"; + + lhs.http_response_code = 2; + EXPECT_NE(rhs, lhs); + lhs.http_response_code = 1; + + lhs.from_http_cache = false; + EXPECT_NE(rhs, lhs); + lhs.from_http_cache = true; + + lhs.content_location_header = "http://other.test-location.com/image.png"; + EXPECT_NE(rhs, lhs); + lhs.content_location_header = "http://test-location.com/image.png"; +} + +} // namespace image_fetcher diff --git a/chromium/components/image_fetcher/image_data_fetcher_unittest.cc b/chromium/components/image_fetcher/image_data_fetcher_unittest.cc deleted file mode 100644 index 15039781fb4..00000000000 --- a/chromium/components/image_fetcher/image_data_fetcher_unittest.cc +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2016 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/image_data_fetcher.h" - -#include <memory> - -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" -#include "net/http/http_response_headers.h" -#include "net/http/http_status_code.h" -#include "net/url_request/test_url_fetcher_factory.h" -#include "net/url_request/url_request_status.h" -#include "net/url_request/url_request_test_util.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -const char kImageURL[] = "http://www.example.com/image"; -const char kURLResponseData[] = "EncodedImageData"; - -} // namespace - -namespace image_fetcher { - -class ImageDataFetcherTest : public testing::Test { - public: - ImageDataFetcherTest() - : test_request_context_getter_( - new net::TestURLRequestContextGetter(message_loop_.task_runner())), - image_data_fetcher_(test_request_context_getter_.get()) {} - ~ImageDataFetcherTest() override {} - - MOCK_METHOD2(OnImageDataFetched, - void(const std::string&, const RequestMetadata&)); - - MOCK_METHOD2(OnImageDataFetchedFailedRequest, - void(const std::string&, const RequestMetadata&)); - - MOCK_METHOD2(OnImageDataFetchedMultipleRequests, - void(const std::string&, const RequestMetadata&)); - - protected: - base::MessageLoop message_loop_; - - scoped_refptr<net::URLRequestContextGetter> test_request_context_getter_; - - ImageDataFetcher image_data_fetcher_; - - net::TestURLFetcherFactory fetcher_factory_; - - private: - DISALLOW_COPY_AND_ASSIGN(ImageDataFetcherTest); -}; - -TEST_F(ImageDataFetcherTest, FetchImageData) { - image_data_fetcher_.FetchImageData( - GURL(kImageURL), - base::Bind(&ImageDataFetcherTest::OnImageDataFetched, - base::Unretained(this))); - - RequestMetadata expected_metadata; - expected_metadata.mime_type = std::string("image/png"); - expected_metadata.response_code = net::HTTP_OK; - EXPECT_CALL(*this, OnImageDataFetched(std::string(kURLResponseData), - expected_metadata)); - - // Get and configure the TestURLFetcher. - net::TestURLFetcher* test_url_fetcher = fetcher_factory_.GetFetcherByID(0); - ASSERT_NE(nullptr, test_url_fetcher); - test_url_fetcher->set_status( - net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK)); - test_url_fetcher->SetResponseString(kURLResponseData); - test_url_fetcher->set_response_code(net::HTTP_OK); - - std::string raw_header = - "HTTP/1.1 200 OK\n" - "Content-type: image/png\n\n"; - std::replace(raw_header.begin(), raw_header.end(), '\n', '\0'); - scoped_refptr<net::HttpResponseHeaders> headers( - new net::HttpResponseHeaders(raw_header)); - - test_url_fetcher->set_response_headers(headers); - - // Call the URLFetcher delegate to continue the test. - test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher); -} - -TEST_F(ImageDataFetcherTest, FetchImageData_NotFound) { - image_data_fetcher_.FetchImageData( - GURL(kImageURL), base::Bind(&ImageDataFetcherTest::OnImageDataFetched, - base::Unretained(this))); - - RequestMetadata expected_metadata; - expected_metadata.mime_type = std::string("image/png"); - expected_metadata.response_code = net::HTTP_NOT_FOUND; - // For 404, expect an empty result even though correct image data is sent. - EXPECT_CALL(*this, OnImageDataFetched(std::string(), expected_metadata)); - - // Get and configure the TestURLFetcher. - net::TestURLFetcher* test_url_fetcher = fetcher_factory_.GetFetcherByID(0); - ASSERT_NE(nullptr, test_url_fetcher); - test_url_fetcher->set_status( - net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK)); - test_url_fetcher->SetResponseString(kURLResponseData); - - std::string raw_header = - "HTTP/1.1 404 Not Found\n" - "Content-type: image/png\n\n"; - std::replace(raw_header.begin(), raw_header.end(), '\n', '\0'); - scoped_refptr<net::HttpResponseHeaders> headers( - new net::HttpResponseHeaders(raw_header)); - - test_url_fetcher->set_response_headers(headers); - - // Call the URLFetcher delegate to continue the test. - test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher); -} - -TEST_F(ImageDataFetcherTest, FetchImageData_FailedRequest) { - image_data_fetcher_.FetchImageData( - GURL(kImageURL), - base::Bind(&ImageDataFetcherTest::OnImageDataFetchedFailedRequest, - base::Unretained(this))); - - RequestMetadata expected_metadata; - expected_metadata.response_code = net::URLFetcher::RESPONSE_CODE_INVALID; - EXPECT_CALL( - *this, OnImageDataFetchedFailedRequest(std::string(), expected_metadata)); - - // Get and configure the TestURLFetcher. - net::TestURLFetcher* test_url_fetcher = fetcher_factory_.GetFetcherByID(0); - ASSERT_NE(nullptr, test_url_fetcher); - test_url_fetcher->set_status( - net::URLRequestStatus(net::URLRequestStatus::FAILED, - net::ERR_INVALID_URL)); - - // Call the URLFetcher delegate to continue the test. - test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher); -} - -TEST_F(ImageDataFetcherTest, FetchImageData_MultipleRequests) { - ImageDataFetcher::ImageDataFetcherCallback callback = - base::Bind(&ImageDataFetcherTest::OnImageDataFetchedMultipleRequests, - base::Unretained(this)); - EXPECT_CALL(*this, OnImageDataFetchedMultipleRequests(testing::_, testing::_)) - .Times(2); - - image_data_fetcher_.FetchImageData(GURL(kImageURL), callback); - image_data_fetcher_.FetchImageData(GURL(kImageURL), callback); - - // Multiple calls to FetchImageData for the same URL will result in - // multiple URLFetchers being created. - net::TestURLFetcher* test_url_fetcher = fetcher_factory_.GetFetcherByID(0); - ASSERT_NE(nullptr, test_url_fetcher); - test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher); - - test_url_fetcher = fetcher_factory_.GetFetcherByID(1); - ASSERT_NE(nullptr, test_url_fetcher); - test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher); -} - -} // namespace image_fetcher diff --git a/chromium/components/image_fetcher/ios/BUILD.gn b/chromium/components/image_fetcher/ios/BUILD.gn index 88d514b151f..9ad5c7edc70 100644 --- a/chromium/components/image_fetcher/ios/BUILD.gn +++ b/chromium/components/image_fetcher/ios/BUILD.gn @@ -6,14 +6,18 @@ source_set("ios") { sources = [ "ios_image_data_fetcher_wrapper.h", "ios_image_data_fetcher_wrapper.mm", + "ios_image_decoder_impl.h", + "ios_image_decoder_impl.mm", "webp_decoder.h", "webp_decoder.mm", ] deps = [ "//base", - "//components/image_fetcher", + "//components/image_fetcher/core", + "//ios/web", "//net", "//third_party/libwebp:libwebp_dec", + "//ui/gfx", ] configs += [ "//build/config/compiler:enable_arc" ] } @@ -22,6 +26,7 @@ source_set("unit_tests") { testonly = true sources = [ "ios_image_data_fetcher_wrapper_unittest.mm", + "ios_image_decoder_impl_unittest.mm", "webp_decoder_unittest.mm", ] deps = [ @@ -32,6 +37,7 @@ source_set("unit_tests") { "//net:test_support", "//testing/gmock", "//testing/gtest", + "//ui/gfx", ] configs += [ "//build/config/compiler:enable_arc" ] } diff --git a/chromium/components/image_fetcher/ios/DEPS b/chromium/components/image_fetcher/ios/DEPS index d2f380fdf1b..dc46f3b0b57 100644 --- a/chromium/components/image_fetcher/ios/DEPS +++ b/chromium/components/image_fetcher/ios/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+ios/web/public", + "+ui/gfx", # Only WebP decoding is allowed (no encoding). "+third_party/libwebp/webp/decode.h", diff --git a/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper.h b/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper.h index 4b6e0b67ef1..57806b0adba 100644 --- a/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper.h +++ b/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper.h @@ -9,7 +9,7 @@ #include "base/memory/ref_counted.h" #include "components/data_use_measurement/core/data_use_user_data.h" -#include "components/image_fetcher/image_data_fetcher.h" +#include "components/image_fetcher/core/image_data_fetcher.h" namespace base { class TaskRunner; diff --git a/chromium/components/image_fetcher/ios/ios_image_decoder_impl.h b/chromium/components/image_fetcher/ios/ios_image_decoder_impl.h new file mode 100644 index 00000000000..2bb5db145d5 --- /dev/null +++ b/chromium/components/image_fetcher/ios/ios_image_decoder_impl.h @@ -0,0 +1,23 @@ +// Copyright 2016 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. + +#ifndef COMPONENTS_IMAGE_FETCHER_IOS_IMAGE_DECODER_IMPL_H_ +#define COMPONENTS_IMAGE_FETCHER_IOS_IMAGE_DECODER_IMPL_H_ + +#include "base/memory/ref_counted.h" +#include "components/image_fetcher/core/image_decoder.h" + +namespace base { +class TaskRunner; +} + +namespace image_fetcher { + +// Factory for iOS specific implementation of ImageDecoder. +std::unique_ptr<ImageDecoder> CreateIOSImageDecoder( + scoped_refptr<base::TaskRunner> task_runner); + +} // namespace image_fetcher + +#endif // COMPONENTS_IMAGE_FETCHER_IOS_IMAGE_DECODER_IMPL_H_ diff --git a/chromium/components/image_fetcher/ios/ios_image_decoder_impl.mm b/chromium/components/image_fetcher/ios/ios_image_decoder_impl.mm new file mode 100644 index 00000000000..ba32199f114 --- /dev/null +++ b/chromium/components/image_fetcher/ios/ios_image_decoder_impl.mm @@ -0,0 +1,106 @@ +// Copyright 2016 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/ios/ios_image_decoder_impl.h" + +#import <UIKit/UIKit.h> + +#include "base/callback.h" +#import "base/mac/bind_objc_block.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/memory/weak_ptr.h" +#import "components/image_fetcher/ios/webp_decoder.h" +#include "ios/web/public/web_thread.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/image/image.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace image_fetcher { + +class IOSImageDecoderImpl : public ImageDecoder { + public: + explicit IOSImageDecoderImpl(scoped_refptr<base::TaskRunner> task_runner); + ~IOSImageDecoderImpl() override; + + // Note, that |desired_image_frame_size| is not supported + // (http://crbug/697596). + void DecodeImage(const std::string& image_data, + const gfx::Size& desired_image_frame_size, + const ImageDecodedCallback& callback) override; + + private: + void CreateUIImageAndRunCallback(const ImageDecodedCallback& callback, + NSData* image_data); + + // The task runner used to decode images if necessary. + const scoped_refptr<base::TaskRunner> task_runner_; + + // The WeakPtrFactory is used to cancel callbacks if ImageFetcher is + // destroyed during WebP decoding. + base::WeakPtrFactory<IOSImageDecoderImpl> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(IOSImageDecoderImpl); +}; + +IOSImageDecoderImpl::IOSImageDecoderImpl( + scoped_refptr<base::TaskRunner> task_runner) + : task_runner_(std::move(task_runner)), weak_factory_(this) { + DCHECK(task_runner_.get()); +} + +IOSImageDecoderImpl::~IOSImageDecoderImpl() {} + +void IOSImageDecoderImpl::DecodeImage(const std::string& image_data, + const gfx::Size& desired_image_frame_size, + const ImageDecodedCallback& callback) { + // Convert the |image_data| std::string to an NSData buffer. + // The data is copied as it may have to outlive the caller in + // PostTaskAndReplyWithResult. + NSData* data = + [NSData dataWithBytes:image_data.data() length:image_data.size()]; + + // The WebP image format is not supported by iOS natively. Therefore WebP + // images need to be decoded explicitly, + if (webp_transcode::WebpDecoder::IsWebpImage(image_data)) { + base::PostTaskAndReplyWithResult( + task_runner_.get(), FROM_HERE, base::BindBlockArc(^NSData*() { + return webp_transcode::WebpDecoder::DecodeWebpImage(data); + }), + base::Bind(&IOSImageDecoderImpl::CreateUIImageAndRunCallback, + weak_factory_.GetWeakPtr(), callback)); + } else { + CreateUIImageAndRunCallback(callback, data); + } +} + +void IOSImageDecoderImpl::CreateUIImageAndRunCallback( + const ImageDecodedCallback& callback, + NSData* image_data) { + // Decode the image data using UIImage. + if (image_data) { + // "Most likely" always returns 1x images. + UIImage* ui_image = [UIImage imageWithData:image_data scale:1]; + if (ui_image) { + // This constructor does not retain the image, but expects to take the + // ownership, therefore, |ui_image| is retained here, but not released + // afterwards. + gfx::Image gfx_image(ui_image, base::scoped_policy::RETAIN); + callback.Run(gfx_image); + return; + } + } + gfx::Image empty_image; + callback.Run(empty_image); +} + +std::unique_ptr<ImageDecoder> CreateIOSImageDecoder( + scoped_refptr<base::TaskRunner> task_runner) { + return base::MakeUnique<IOSImageDecoderImpl>(std::move(task_runner)); +} + +} // namespace image_fetcher diff --git a/chromium/components/image_fetcher/ios/ios_image_decoder_impl_unittest.mm b/chromium/components/image_fetcher/ios/ios_image_decoder_impl_unittest.mm new file mode 100644 index 00000000000..b83a127b3aa --- /dev/null +++ b/chromium/components/image_fetcher/ios/ios_image_decoder_impl_unittest.mm @@ -0,0 +1,112 @@ +// Copyright 2011 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/ios/ios_image_decoder_impl.h" + +#import <UIKit/UIKit.h> + +#include "base/bind.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "base/threading/sequenced_worker_pool.h" +#include "base/threading/thread_task_runner_handle.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/image/image.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace { + +static unsigned char kJPGImage[] = { + 255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 1, 0, + 72, 0, 72, 0, 0, 255, 254, 0, 19, 67, 114, 101, 97, 116, 101, + 100, 32, 119, 105, 116, 104, 32, 71, 73, 77, 80, 255, 219, 0, 67, + 0, 5, 3, 4, 4, 4, 3, 5, 4, 4, 4, 5, 5, 5, 6, + 7, 12, 8, 7, 7, 7, 7, 15, 11, 11, 9, 12, 17, 15, 18, + 18, 17, 15, 17, 17, 19, 22, 28, 23, 19, 20, 26, 21, 17, 17, + 24, 33, 24, 26, 29, 29, 31, 31, 31, 19, 23, 34, 36, 34, 30, + 36, 28, 30, 31, 30, 255, 219, 0, 67, 1, 5, 5, 5, 7, 6, + 7, 14, 8, 8, 14, 30, 20, 17, 20, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 255, + 192, 0, 17, 8, 0, 1, 0, 1, 3, 1, 34, 0, 2, 17, 1, + 3, 17, 1, 255, 196, 0, 21, 0, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 196, 0, 20, + 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 196, 0, 20, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 196, 0, 20, 17, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 218, 0, 12, 3, 1, 0, 2, 17, 3, 17, 0, 63, + 0, 178, 192, 7, 255, 217}; + +static unsigned char kWEBPImage[] = { + 82, 73, 70, 70, 74, 0, 0, 0, 87, 69, 66, 80, 86, 80, 56, 88, 10, + 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 76, 80, 72, + 12, 0, 0, 0, 1, 7, 16, 17, 253, 15, 68, 68, 255, 3, 0, 0, 86, + 80, 56, 32, 24, 0, 0, 0, 48, 1, 0, 157, 1, 42, 1, 0, 1, 0, + 3, 0, 52, 37, 164, 0, 3, 112, 0, 254, 251, 253, 80, 0}; + +} // namespace + +namespace image_fetcher { + +class IOSImageDecoderImplTest : public PlatformTest { + public: + void OnImageDecoded(const gfx::Image& image) { decoded_image_ = image; } + + protected: + IOSImageDecoderImplTest() + : pool_(new base::SequencedWorkerPool(2, + "TestPool", + base::TaskPriority::USER_VISIBLE)) { + ios_image_decoder_impl_ = CreateIOSImageDecoder(pool_); + } + + ~IOSImageDecoderImplTest() override { pool_->Shutdown(); } + + base::MessageLoop loop_; + scoped_refptr<base::SequencedWorkerPool> pool_; + std::unique_ptr<ImageDecoder> ios_image_decoder_impl_; + + gfx::Image decoded_image_; +}; + +TEST_F(IOSImageDecoderImplTest, JPGImage) { + ASSERT_TRUE(decoded_image_.IsEmpty()); + + std::string image_data = + std::string(reinterpret_cast<char*>(kJPGImage), sizeof(kJPGImage)); + ios_image_decoder_impl_->DecodeImage( + image_data, gfx::Size(), + base::Bind(&IOSImageDecoderImplTest::OnImageDecoded, + base::Unretained(this))); + + pool_->FlushForTesting(); + base::RunLoop().RunUntilIdle(); + + EXPECT_FALSE(decoded_image_.IsEmpty()); +} + +TEST_F(IOSImageDecoderImplTest, WebpImage) { + ASSERT_TRUE(decoded_image_.IsEmpty()); + + std::string image_data = + std::string(reinterpret_cast<char*>(kWEBPImage), sizeof(kWEBPImage)); + ios_image_decoder_impl_->DecodeImage( + image_data, gfx::Size(), + base::Bind(&IOSImageDecoderImplTest::OnImageDecoded, + base::Unretained(this))); + + pool_->FlushForTesting(); + base::RunLoop().RunUntilIdle(); + + EXPECT_FALSE(decoded_image_.IsEmpty()); +} + +} // namespace image_fetcher diff --git a/chromium/components/image_fetcher/request_metadata.h b/chromium/components/image_fetcher/request_metadata.h deleted file mode 100644 index 6b41e2d3c31..00000000000 --- a/chromium/components/image_fetcher/request_metadata.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2017 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. - -#ifndef COMPONENTS_IMAGE_FETCHER_REQUEST_METADATA_H_ -#define COMPONENTS_IMAGE_FETCHER_REQUEST_METADATA_H_ - -#include <string> - -namespace image_fetcher { - -// Metadata for a URL request. -struct RequestMetadata { - std::string mime_type; - // HTTP response code. - int response_code; -}; - -bool operator==(const RequestMetadata& lhs, const RequestMetadata& rhs); -bool operator!=(const RequestMetadata& lhs, const RequestMetadata& rhs); - -} // namespace image_fetcher - -#endif // COMPONENTS_IMAGE_FETCHER_REQUEST_METADATA_H_ diff --git a/chromium/components/image_fetcher/request_metadata_unittest.cc b/chromium/components/image_fetcher/request_metadata_unittest.cc deleted file mode 100644 index ba87ba08d8f..00000000000 --- a/chromium/components/image_fetcher/request_metadata_unittest.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2017 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/request_metadata.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace image_fetcher { - -TEST(RequestMetadataTest, Equality) { - RequestMetadata rhs; - RequestMetadata lhs; - rhs.mime_type = "testMimeType"; - lhs.mime_type = "testMimeType"; - rhs.response_code = 1; - lhs.response_code = 1; - - EXPECT_EQ(rhs, lhs); -} - -TEST(RequestMetadataTest, NoEquality) { - RequestMetadata rhs; - RequestMetadata lhs; - rhs.mime_type = "testMimeType"; - lhs.mime_type = "testMimeType"; - rhs.response_code = 1; - lhs.response_code = 1; - - lhs.mime_type = "testOtherMimeType"; - EXPECT_NE(rhs, lhs); - lhs.mime_type = "testMimeType"; - - lhs.response_code = 2; - EXPECT_NE(rhs, lhs); - lhs.response_code = 1; -} - -} // namespace image_fetcher |