Adds service to get metadata about images to display on NTP background.
Creates a service that uses SimpleURLLoader to asynchronously request
metadata about images from a specified collection, and makes them
available to the Local NTP in a javascript variable.
Bug: 839152
Change-Id: Ifc2d14bd451f02c42542b3cd5a50bef5c50eb0d4
Reviewed-on: https://chromium-review.googlesource.com/1074339
Commit-Queue: Ramya Nagarajan <[email protected]>
Reviewed-by: Ilya Sherman <[email protected]>
Reviewed-by: Marc Treib <[email protected]>
Reviewed-by: Christian Dullweber <[email protected]>
Cr-Commit-Position: refs/heads/master@{#562736}
diff --git a/chrome/browser/search/background/ntp_background.proto b/chrome/browser/search/background/ntp_background.proto
index 1821c1a9..9d30eca 100644
--- a/chrome/browser/search/background/ntp_background.proto
+++ b/chrome/browser/search/background/ntp_background.proto
@@ -19,7 +19,7 @@
}
message Image {
- // A unique ID for the image. Useful for analytics.
+ // A unique ID for the image.
optional fixed64 asset_id = 1;
// The image URL.
@@ -62,3 +62,23 @@
// A list of every available collection for the given language and region.
repeated Collection collections = 1;
}
+
+message GetImagesInCollectionRequest {
+ // Deprecated or unused tag numbers
+ reserved 4;
+
+ // The id of the collection being requested.
+ optional string collection_id = 1;
+
+ // The language that should be used for content. e.g. "en-US"
+ optional string language = 2;
+
+ // The approximate permanent location of the user e.g. "us".
+ optional string region = 3;
+}
+
+message GetImagesInCollectionResponse {
+ // A list of all the images in the requested collection, filtered by language
+ // and region.
+ repeated Image images = 1;
+}
diff --git a/chrome/browser/search/background/ntp_background_data.cc b/chrome/browser/search/background/ntp_background_data.cc
index 728ae9f..57ca655 100644
--- a/chrome/browser/search/background/ntp_background_data.cc
+++ b/chrome/browser/search/background/ntp_background_data.cc
@@ -34,3 +34,35 @@
return collection_info;
}
+
+CollectionImage::CollectionImage() = default;
+CollectionImage::CollectionImage(const CollectionImage&) = default;
+CollectionImage::CollectionImage(CollectionImage&&) = default;
+CollectionImage::~CollectionImage() = default;
+
+CollectionImage& CollectionImage::operator=(const CollectionImage&) = default;
+CollectionImage& CollectionImage::operator=(CollectionImage&&) = default;
+
+bool operator==(const CollectionImage& lhs, const CollectionImage& rhs) {
+ return lhs.collection_id == rhs.collection_id &&
+ lhs.asset_id == rhs.asset_id && lhs.image_url == rhs.image_url &&
+ lhs.attribution == rhs.attribution;
+}
+
+bool operator!=(const CollectionImage& lhs, const CollectionImage& rhs) {
+ return !(lhs == rhs);
+}
+
+CollectionImage CollectionImage::CreateFromProto(
+ std::string collection_id,
+ const ntp::background::Image& image) {
+ CollectionImage collection_image;
+ collection_image.collection_id = collection_id;
+ collection_image.asset_id = image.asset_id();
+ collection_image.image_url = GURL(image.image_url());
+ for (const auto& attribution : image.attribution()) {
+ collection_image.attribution.push_back(attribution.text());
+ }
+
+ return collection_image;
+}
diff --git a/chrome/browser/search/background/ntp_background_data.h b/chrome/browser/search/background/ntp_background_data.h
index 9e0e543f..a0bfd66 100644
--- a/chrome/browser/search/background/ntp_background_data.h
+++ b/chrome/browser/search/background/ntp_background_data.h
@@ -12,8 +12,8 @@
// Background images are organized into collections, according to a theme. This
// struct contains the data required to display information about a collection,
-// including a representative image. The complete set of images must be
-// requested separately, by referencing the identifier for this collection.
+// including a representative image. The complete set of CollectionImages must
+// be requested separately, by referencing the identifier for this collection.
struct CollectionInfo {
CollectionInfo();
CollectionInfo(const CollectionInfo&);
@@ -37,4 +37,31 @@
bool operator==(const CollectionInfo& lhs, const CollectionInfo& rhs);
bool operator!=(const CollectionInfo& lhs, const CollectionInfo& rhs);
+// Represents an image within a collection. The associated collection_id may be
+// used to get CollectionInfo.
+struct CollectionImage {
+ CollectionImage();
+ CollectionImage(const CollectionImage&);
+ CollectionImage(CollectionImage&&);
+ ~CollectionImage();
+
+ CollectionImage& operator=(const CollectionImage&);
+ CollectionImage& operator=(CollectionImage&&);
+
+ static CollectionImage CreateFromProto(std::string collection_id,
+ const ntp::background::Image& image);
+
+ // A unique identifier for the collection the image is in.
+ std::string collection_id;
+ // A unique identifier for the image.
+ uint64_t asset_id;
+ // The image URL.
+ GURL image_url;
+ // The attribution list for the image.
+ std::vector<std::string> attribution;
+};
+
+bool operator==(const CollectionImage& lhs, const CollectionImage& rhs);
+bool operator!=(const CollectionImage& lhs, const CollectionImage& rhs);
+
#endif // CHROME_BROWSER_SEARCH_BACKGROUND_NTP_BACKGROUND_DATA_H_
diff --git a/chrome/browser/search/background/ntp_background_service.cc b/chrome/browser/search/background/ntp_background_service.cc
index 844cd478..b9609ef 100644
--- a/chrome/browser/search/background/ntp_background_service.cc
+++ b/chrome/browser/search/background/ntp_background_service.cc
@@ -19,24 +19,31 @@
constexpr char kProtoMimeType[] = "application/x-protobuf";
// The url to download the proto of the complete list of wallpaper collections.
-constexpr char kBackdropCollectionsUrl[] =
+constexpr char kCollectionsUrl[] =
"https://clients3.google.com/cast/chromecast/home/wallpaper/"
"collections?rt=b";
-
+// The url to download the metadata of the images in a collection.
+constexpr char kCollectionImagesUrl[] =
+ "https://clients3.google.com/cast/chromecast/home/wallpaper/"
+ "collection-images?rt=b";
} // namespace
NtpBackgroundService::NtpBackgroundService(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
- const base::Optional<GURL>& api_url_override)
+ const base::Optional<GURL>& collections_api_url_override,
+ const base::Optional<GURL>& collection_images_api_url_override)
: url_loader_factory_(url_loader_factory) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- api_url_ = api_url_override.value_or(GURL(kBackdropCollectionsUrl));
+ collections_api_url_ =
+ collections_api_url_override.value_or(GURL(kCollectionsUrl));
+ collection_images_api_url_ =
+ collection_images_api_url_override.value_or(GURL(kCollectionImagesUrl));
}
NtpBackgroundService::~NtpBackgroundService() = default;
void NtpBackgroundService::FetchCollectionInfo() {
- if (simple_loader_ != nullptr)
+ if (collections_loader_ != nullptr)
return;
net::NetworkTrafficAnnotationTag traffic_annotation =
@@ -76,14 +83,14 @@
request.SerializeToString(&serialized_proto);
auto resource_request = std::make_unique<network::ResourceRequest>();
- resource_request->url = api_url_;
+ resource_request->url = collections_api_url_;
resource_request->method = "POST";
resource_request->load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA;
- simple_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
- traffic_annotation);
- simple_loader_->AttachStringForUpload(serialized_proto, kProtoMimeType);
- simple_loader_->DownloadToString(
+ collections_loader_ = network::SimpleURLLoader::Create(
+ std::move(resource_request), traffic_annotation);
+ collections_loader_->AttachStringForUpload(serialized_proto, kProtoMimeType);
+ collections_loader_->DownloadToString(
url_loader_factory_.get(),
base::BindOnce(&NtpBackgroundService::OnCollectionInfoFetchComplete,
base::Unretained(this)),
@@ -95,14 +102,14 @@
collection_info_.clear();
// The loader will be deleted when the request is handled.
std::unique_ptr<network::SimpleURLLoader> loader_deleter(
- std::move(simple_loader_));
+ std::move(collections_loader_));
if (!response_body) {
// This represents network errors (i.e. the server did not provide a
// response).
DLOG(WARNING) << "Request failed with error: "
<< loader_deleter->NetError();
- NotifyObservers();
+ NotifyObservers(FetchComplete::COLLECTION_INFO);
return;
}
@@ -111,7 +118,7 @@
DLOG(WARNING)
<< "Deserializing Backdrop wallpaper proto for collection info "
"failed.";
- NotifyObservers();
+ NotifyObservers(FetchComplete::COLLECTION_INFO);
return;
}
@@ -120,7 +127,97 @@
CollectionInfo::CreateFromProto(collections_response.collections(i)));
}
- NotifyObservers();
+ NotifyObservers(FetchComplete::COLLECTION_INFO);
+}
+
+void NtpBackgroundService::FetchCollectionImageInfo(
+ const std::string& collection_id) {
+ if (image_info_loader_ != nullptr)
+ return;
+
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("backdrop_collection_images_download",
+ R"(
+ semantics {
+ sender: "Desktop NTP Background Selector"
+ description:
+ "The Chrome Desktop New Tab Page background selector displays a "
+ "rich set of wallpapers for users to choose from. Each wallpaper "
+ "belongs to a collection (e.g. Arts, Landscape etc.). The list of "
+ "all available collections is obtained from the Backdrop wallpaper "
+ "service."
+ trigger:
+ "Clicking the the settings (gear) icon on the New Tab page."
+ data:
+ "The Backdrop protocol buffer messages. No user data is included."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: NO
+ setting:
+ "Users can control this feature by selecting a non-Google default "
+ "search engine in Chrome settings under 'Search Engine'."
+ chrome_policy {
+ DefaultSearchProviderEnabled {
+ policy_options {mode: MANDATORY}
+ DefaultSearchProviderEnabled: false
+ }
+ }
+ })");
+
+ requested_collection_id_ = collection_id;
+ ntp::background::GetImagesInCollectionRequest request;
+ request.set_collection_id(collection_id);
+ // The language field may include the country code (e.g. "en-US").
+ request.set_language(g_browser_process->GetApplicationLocale());
+ std::string serialized_proto;
+ request.SerializeToString(&serialized_proto);
+
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = collection_images_api_url_;
+ resource_request->method = "POST";
+ resource_request->load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA;
+
+ image_info_loader_ = network::SimpleURLLoader::Create(
+ std::move(resource_request), traffic_annotation);
+ image_info_loader_->AttachStringForUpload(serialized_proto, kProtoMimeType);
+ image_info_loader_->DownloadToString(
+ url_loader_factory_.get(),
+ base::BindOnce(&NtpBackgroundService::OnCollectionImageInfoFetchComplete,
+ base::Unretained(this)),
+ 1024 * 1024);
+}
+
+void NtpBackgroundService::OnCollectionImageInfoFetchComplete(
+ std::unique_ptr<std::string> response_body) {
+ collection_images_.clear();
+ // The loader will be deleted when the request is handled.
+ std::unique_ptr<network::SimpleURLLoader> loader_deleter(
+ std::move(image_info_loader_));
+
+ if (!response_body) {
+ // This represents network errors (i.e. the server did not provide a
+ // response).
+ DLOG(WARNING) << "Request failed with error: "
+ << loader_deleter->NetError();
+ NotifyObservers(FetchComplete::COLLECTION_IMAGE_INFO);
+ return;
+ }
+
+ ntp::background::GetImagesInCollectionResponse images_response;
+ if (!images_response.ParseFromString(*response_body)) {
+ DLOG(WARNING)
+ << "Deserializing Backdrop wallpaper proto for image info failed.";
+ NotifyObservers(FetchComplete::COLLECTION_IMAGE_INFO);
+ return;
+ }
+
+ for (int i = 0; i < images_response.images_size(); ++i) {
+ collection_images_.push_back(CollectionImage::CreateFromProto(
+ requested_collection_id_, images_response.images(i)));
+ }
+
+ NotifyObservers(FetchComplete::COLLECTION_IMAGE_INFO);
}
void NtpBackgroundService::AddObserver(NtpBackgroundServiceObserver* observer) {
@@ -132,12 +229,23 @@
observers_.RemoveObserver(observer);
}
-void NtpBackgroundService::NotifyObservers() {
+void NtpBackgroundService::NotifyObservers(FetchComplete fetch_complete) {
for (auto& observer : observers_) {
- observer.OnCollectionInfoAvailable();
+ switch (fetch_complete) {
+ case FetchComplete::COLLECTION_INFO:
+ observer.OnCollectionInfoAvailable();
+ break;
+ case FetchComplete::COLLECTION_IMAGE_INFO:
+ observer.OnCollectionImagesAvailable();
+ break;
+ }
}
}
-GURL NtpBackgroundService::GetLoadURLForTesting() const {
- return api_url_;
+GURL NtpBackgroundService::GetCollectionsLoadURLForTesting() const {
+ return collections_api_url_;
+}
+
+GURL NtpBackgroundService::GetImagesURLForTesting() const {
+ return collection_images_api_url_;
}
diff --git a/chrome/browser/search/background/ntp_background_service.h b/chrome/browser/search/background/ntp_background_service.h
index bbc7bba..3ba90541 100644
--- a/chrome/browser/search/background/ntp_background_service.h
+++ b/chrome/browser/search/background/ntp_background_service.h
@@ -28,43 +28,73 @@
public:
NtpBackgroundService(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
- const base::Optional<GURL>& api_url_override);
+ const base::Optional<GURL>& collections_api_url_override,
+ const base::Optional<GURL>& collection_images_api_url_override);
~NtpBackgroundService() override;
// Requests an asynchronous fetch from the network. After the update
- // completes, OnCollectionInfoUpdated will be called on the observers.
+ // completes, OnCollectionInfoAvailable will be called on the observers.
void FetchCollectionInfo();
- // Returns the currently cached CollectionInfo, if any.
- const std::vector<CollectionInfo>& collection_info() const {
- return collection_info_;
- }
+ // Requests an asynchronous fetch of metadata about images in the specified
+ // collection. After the update completes, OnCollectionImagesAvailable will be
+ // called on the observers. Requests that are made while an asynchronous fetch
+ // is in progress will be dropped until the currently active loader completes.
+ void FetchCollectionImageInfo(const std::string& collection_id);
// Add/remove observers. All observers must unregister themselves before the
// NtpBackgroundService is destroyed.
void AddObserver(NtpBackgroundServiceObserver* observer);
void RemoveObserver(NtpBackgroundServiceObserver* observer);
- GURL GetLoadURLForTesting() const;
+ // Returns the currently cached CollectionInfo, if any.
+ const std::vector<CollectionInfo>& collection_info() const {
+ return collection_info_;
+ }
+
+ // Returns the currently cached CollectionImages, if any.
+ const std::vector<CollectionImage>& collection_images() const {
+ return collection_images_;
+ }
+
+ GURL GetCollectionsLoadURLForTesting() const;
+ GURL GetImagesURLForTesting() const;
private:
- GURL api_url_;
+ GURL collections_api_url_;
+ GURL collection_images_api_url_;
// Used to download the proto from the Backdrop service.
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
- std::unique_ptr<network::SimpleURLLoader> simple_loader_;
+ std::unique_ptr<network::SimpleURLLoader> collections_loader_;
+ std::unique_ptr<network::SimpleURLLoader> image_info_loader_;
base::ObserverList<NtpBackgroundServiceObserver, true> observers_;
// Callback that processes the response from the FetchCollectionInfo request,
- // refreshing the contents of collection_info_data_ with server-provided data.
+ // refreshing the contents of collection_info_ with server-provided data.
void OnCollectionInfoFetchComplete(
const std::unique_ptr<std::string> response_body);
- void NotifyObservers();
+ // Callback that processes the response from the FetchCollectionImages
+ // request, refreshing the contents of collection_images_ with
+ // server-provided data.
+ void OnCollectionImageInfoFetchComplete(
+ const std::unique_ptr<std::string> response_body);
+
+ enum class FetchComplete {
+ // Indicates that asynchronous fetch of CollectionInfo has completed.
+ COLLECTION_INFO,
+ // Indicates that asynchronous fetch of CollectionImages has completed.
+ COLLECTION_IMAGE_INFO
+ };
+ void NotifyObservers(FetchComplete fetch_complete);
std::vector<CollectionInfo> collection_info_;
+ std::vector<CollectionImage> collection_images_;
+ std::string requested_collection_id_;
+
DISALLOW_COPY_AND_ASSIGN(NtpBackgroundService);
};
diff --git a/chrome/browser/search/background/ntp_background_service_factory.cc b/chrome/browser/search/background/ntp_background_service_factory.cc
index 1d6aecc3..ac365ebb 100644
--- a/chrome/browser/search/background/ntp_background_service_factory.cc
+++ b/chrome/browser/search/background/ntp_background_service_factory.cc
@@ -42,15 +42,24 @@
return nullptr;
}
- std::string api_url = base::GetFieldTrialParamValueByFeature(
+ std::string collections_api_url = base::GetFieldTrialParamValueByFeature(
features::kNtpBackgrounds, "background-collections-api-url");
- base::Optional<GURL> override_api_url;
- if (!api_url.empty()) {
- override_api_url = GURL(api_url);
+ std::string collection_images_api_url =
+ base::GetFieldTrialParamValueByFeature(
+ features::kNtpBackgrounds, "background-collection-images-api-url");
+ base::Optional<GURL> collection_api_url_override;
+ base::Optional<GURL> collection_images_api_url_override;
+ if (!collections_api_url.empty()) {
+ collection_api_url_override = GURL(collections_api_url);
+ }
+ if (!collection_images_api_url.empty()) {
+ collection_images_api_url_override = GURL(collection_images_api_url);
}
auto url_loader_factory =
content::BrowserContext::GetDefaultStoragePartition(context)
->GetURLLoaderFactoryForBrowserProcess();
- return new NtpBackgroundService(url_loader_factory, override_api_url);
+ return new NtpBackgroundService(url_loader_factory,
+ collection_api_url_override,
+ collection_images_api_url_override);
}
diff --git a/chrome/browser/search/background/ntp_background_service_observer.h b/chrome/browser/search/background/ntp_background_service_observer.h
index 017a3a8..8c99c710 100644
--- a/chrome/browser/search/background/ntp_background_service_observer.h
+++ b/chrome/browser/search/background/ntp_background_service_observer.h
@@ -8,12 +8,19 @@
// Observer for NtpBackgroundService.
class NtpBackgroundServiceObserver {
public:
- // Called when the CollectionInfoData is updated, usually as the result of a
+ // Called when the CollectionInfo is updated, usually as the result of a
// FetchCollectionInfo() call on the service. Note that this is called after
// each FetchCollectionInfo(), even if the network request failed, or if it
// didn't result in an actual change to the cached data. You can get the new
- // data via NtpBackgroundService::collection_info_data().
+ // data via NtpBackgroundService::collection_info().
virtual void OnCollectionInfoAvailable() = 0;
+
+ // Called when the CollectionImages are updated, usually as the result of a
+ // FetchCollectionImageInfo() call on the service. Note that this is called
+ // after each FetchCollectionImage(), even if the network request failed, or
+ // if it didn't result in an actual change to the cached data. You can get the
+ // new data via NtpBackgroundService::collection_images().
+ virtual void OnCollectionImagesAvailable() = 0;
};
#endif // CHROME_BROWSER_SEARCH_BACKGROUND_NTP_BACKGROUND_SERVICE_OBSERVER_H_
diff --git a/chrome/browser/search/background/ntp_background_service_unittest.cc b/chrome/browser/search/background/ntp_background_service_unittest.cc
index 9bd7861..4e1ba3f 100644
--- a/chrome/browser/search/background/ntp_background_service_unittest.cc
+++ b/chrome/browser/search/background/ntp_background_service_unittest.cc
@@ -18,6 +18,8 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
+using testing::Eq;
+
class NtpBackgroundServiceTest : public testing::Test {
public:
NtpBackgroundServiceTest()
@@ -32,20 +34,20 @@
testing::Test::SetUp();
service_ = std::make_unique<NtpBackgroundService>(
- test_shared_loader_factory_, base::nullopt);
+ test_shared_loader_factory_, base::nullopt, base::nullopt);
}
- void SetUpResponseWithData(const std::string& response) {
+ void SetUpResponseWithData(const GURL& load_url,
+ const std::string& response) {
test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting(
[&](const network::ResourceRequest& request) {}));
- test_url_loader_factory_.AddResponse(
- service_->GetLoadURLForTesting().spec(), response);
+ test_url_loader_factory_.AddResponse(load_url.spec(), response);
}
- void SetUpResponseWithNetworkError() {
+ void SetUpResponseWithNetworkError(const GURL& load_url) {
test_url_loader_factory_.AddResponse(
- service_->GetLoadURLForTesting(), network::ResourceResponseHead(),
- std::string(), network::URLLoaderCompletionStatus(net::HTTP_NOT_FOUND));
+ load_url, network::ResourceResponseHead(), std::string(),
+ network::URLLoaderCompletionStatus(net::HTTP_NOT_FOUND));
}
NtpBackgroundService* service() { return service_.get(); }
@@ -61,27 +63,26 @@
};
TEST_F(NtpBackgroundServiceTest, CollectionInfoNetworkError) {
- SetUpResponseWithNetworkError();
+ SetUpResponseWithNetworkError(service()->GetCollectionsLoadURLForTesting());
ASSERT_TRUE(service()->collection_info().empty());
base::RunLoop loop;
- loop.QuitWhenIdle();
service()->FetchCollectionInfo();
- loop.Run();
+ loop.RunUntilIdle();
ASSERT_TRUE(service()->collection_info().empty());
}
TEST_F(NtpBackgroundServiceTest, BadCollectionsResponse) {
- SetUpResponseWithData("bad serialized GetCollectionsResponse");
+ SetUpResponseWithData(service()->GetCollectionsLoadURLForTesting(),
+ "bad serialized GetCollectionsResponse");
ASSERT_TRUE(service()->collection_info().empty());
base::RunLoop loop;
- loop.QuitWhenIdle();
service()->FetchCollectionInfo();
- loop.Run();
+ loop.RunUntilIdle();
EXPECT_TRUE(service()->collection_info().empty());
}
@@ -90,20 +91,20 @@
ntp::background::Collection collection;
collection.set_collection_id("shapes");
collection.set_collection_name("Shapes");
- collection.add_preview()->set_image_url("image url");
+ collection.add_preview()->set_image_url("https://wallpapers.co/some_image");
ntp::background::GetCollectionsResponse response;
*response.add_collections() = collection;
std::string response_string;
response.SerializeToString(&response_string);
- SetUpResponseWithData(response_string);
+ SetUpResponseWithData(service()->GetCollectionsLoadURLForTesting(),
+ response_string);
ASSERT_TRUE(service()->collection_info().empty());
base::RunLoop loop;
- loop.QuitWhenIdle();
service()->FetchCollectionInfo();
- loop.Run();
+ loop.RunUntilIdle();
CollectionInfo collection_info;
collection_info.collection_id = collection.collection_id();
@@ -111,5 +112,109 @@
collection_info.preview_image_url = GURL(collection.preview(0).image_url());
EXPECT_FALSE(service()->collection_info().empty());
- EXPECT_THAT(service()->collection_info().at(0), testing::Eq(collection_info));
+ EXPECT_THAT(service()->collection_info().at(0), Eq(collection_info));
+}
+
+TEST_F(NtpBackgroundServiceTest, CollectionImagesNetworkError) {
+ SetUpResponseWithNetworkError(service()->GetImagesURLForTesting());
+
+ ASSERT_TRUE(service()->collection_images().empty());
+
+ base::RunLoop loop;
+ service()->FetchCollectionImageInfo("shapes");
+ loop.RunUntilIdle();
+
+ ASSERT_TRUE(service()->collection_images().empty());
+}
+
+TEST_F(NtpBackgroundServiceTest, BadCollectionImagesResponse) {
+ SetUpResponseWithData(service()->GetImagesURLForTesting(),
+ "bad serialized GetImagesInCollectionResponse");
+
+ ASSERT_TRUE(service()->collection_images().empty());
+
+ base::RunLoop loop;
+ service()->FetchCollectionImageInfo("shapes");
+ loop.RunUntilIdle();
+
+ EXPECT_TRUE(service()->collection_images().empty());
+}
+
+TEST_F(NtpBackgroundServiceTest, GoodCollectionImagesResponse) {
+ ntp::background::Image image;
+ image.set_asset_id(12345);
+ image.set_image_url("https://wallpapers.co/some_image");
+ image.add_attribution()->set_text("attribution text");
+ ntp::background::GetImagesInCollectionResponse response;
+ *response.add_images() = image;
+ std::string response_string;
+ response.SerializeToString(&response_string);
+
+ SetUpResponseWithData(service()->GetImagesURLForTesting(), response_string);
+
+ ASSERT_TRUE(service()->collection_images().empty());
+
+ base::RunLoop loop;
+ service()->FetchCollectionImageInfo("shapes");
+ loop.RunUntilIdle();
+
+ CollectionImage collection_image;
+ collection_image.collection_id = "shapes";
+ collection_image.asset_id = image.asset_id();
+ collection_image.image_url = GURL(image.image_url());
+ collection_image.attribution.push_back(image.attribution(0).text());
+
+ EXPECT_FALSE(service()->collection_images().empty());
+ EXPECT_THAT(service()->collection_images().at(0), Eq(collection_image));
+}
+
+TEST_F(NtpBackgroundServiceTest, MultipleRequests) {
+ ntp::background::Collection collection;
+ collection.set_collection_id("shapes");
+ collection.set_collection_name("Shapes");
+ collection.add_preview()->set_image_url("https://wallpapers.co/some_image");
+ ntp::background::GetCollectionsResponse collection_response;
+ *collection_response.add_collections() = collection;
+ std::string collection_response_string;
+ collection_response.SerializeToString(&collection_response_string);
+
+ ntp::background::Image image;
+ image.set_asset_id(12345);
+ image.set_image_url("https://wallpapers.co/some_image");
+ image.add_attribution()->set_text("attribution text");
+ ntp::background::GetImagesInCollectionResponse image_response;
+ *image_response.add_images() = image;
+ std::string image_response_string;
+ image_response.SerializeToString(&image_response_string);
+
+ SetUpResponseWithData(service()->GetCollectionsLoadURLForTesting(),
+ collection_response_string);
+ SetUpResponseWithData(service()->GetImagesURLForTesting(),
+ image_response_string);
+
+ ASSERT_TRUE(service()->collection_info().empty());
+ ASSERT_TRUE(service()->collection_images().empty());
+
+ base::RunLoop loop;
+ service()->FetchCollectionInfo();
+ service()->FetchCollectionImageInfo("shapes");
+ // Subsequent requests are ignored while the loader is in use.
+ service()->FetchCollectionImageInfo("colors");
+ loop.RunUntilIdle();
+
+ CollectionInfo collection_info;
+ collection_info.collection_id = collection.collection_id();
+ collection_info.collection_name = collection.collection_name();
+ collection_info.preview_image_url = GURL(collection.preview(0).image_url());
+
+ CollectionImage collection_image;
+ collection_image.collection_id = "shapes";
+ collection_image.asset_id = image.asset_id();
+ collection_image.image_url = GURL(image.image_url());
+ collection_image.attribution.push_back(image.attribution(0).text());
+
+ EXPECT_FALSE(service()->collection_info().empty());
+ EXPECT_THAT(service()->collection_info().at(0), Eq(collection_info));
+ EXPECT_FALSE(service()->collection_images().empty());
+ EXPECT_THAT(service()->collection_images().at(0), Eq(collection_image));
}
diff --git a/chrome/browser/search/local_ntp_source.cc b/chrome/browser/search/local_ntp_source.cc
index 1422a6b..ddcd762 100644
--- a/chrome/browser/search/local_ntp_source.cc
+++ b/chrome/browser/search/local_ntp_source.cc
@@ -74,6 +74,7 @@
const char kMainHtmlFilename[] = "local-ntp.html";
const char kNtpBackgroundCollectionScriptFilename[] =
"ntp-background-collections.js";
+const char kNtpBackgroundImageScriptFilename[] = "ntp-background-images.js";
const char kOneGoogleBarScriptFilename[] = "one-google.js";
const char kDoodleScriptFilename[] = "doodle.js";
@@ -94,6 +95,7 @@
{"images/close_3_mask.png", IDR_CLOSE_3_MASK, "image/png"},
{"images/ntp_default_favicon.png", IDR_NTP_DEFAULT_FAVICON, "image/png"},
{kNtpBackgroundCollectionScriptFilename, kLocalResource, "text/javascript"},
+ {kNtpBackgroundImageScriptFilename, kLocalResource, "text/javascript"},
{kOneGoogleBarScriptFilename, kLocalResource, "text/javascript"},
{kDoodleScriptFilename, kLocalResource, "text/javascript"},
};
@@ -220,10 +222,10 @@
}
base::Value ConvertCollectionInfoToDict(
- const std::vector<CollectionInfo> collection_info_data) {
+ const std::vector<CollectionInfo>& collection_info) {
base::Value collections(base::Value::Type::LIST);
- collections.GetList().reserve(collection_info_data.size());
- for (const CollectionInfo& collection : collection_info_data) {
+ collections.GetList().reserve(collection_info.size());
+ for (const CollectionInfo& collection : collection_info) {
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetKey("collectionId", base::Value(collection.collection_id));
dict.SetKey("collectionName", base::Value(collection.collection_name));
@@ -234,6 +236,23 @@
return collections;
}
+base::Value ConvertCollectionImageToDict(
+ const std::vector<CollectionImage>& collection_image) {
+ base::Value images(base::Value::Type::LIST);
+ images.GetList().reserve(collection_image.size());
+ for (const CollectionImage& image : collection_image) {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetKey("imageUrl", base::Value(image.image_url.spec()));
+ base::Value attributions(base::Value::Type::LIST);
+ for (const auto& attribution : image.attribution) {
+ attributions.GetList().push_back(base::Value(attribution));
+ }
+ dict.SetKey("attributions", std::move(attributions));
+ images.GetList().push_back(std::move(dict));
+ }
+ return images;
+}
+
std::unique_ptr<base::DictionaryValue> ConvertOGBDataToDict(
const OneGoogleBarData& og) {
auto result = std::make_unique<base::DictionaryValue>();
@@ -568,11 +587,30 @@
return;
}
- ntp_background_requests_.emplace_back(base::TimeTicks::Now(), callback);
+ ntp_background_collections_requests_.emplace_back(base::TimeTicks::Now(),
+ callback);
ntp_background_service_->FetchCollectionInfo();
return;
}
+ if (stripped_path == kNtpBackgroundImageScriptFilename) {
+ if (!ntp_background_service_) {
+ callback.Run(nullptr);
+ return;
+ }
+ std::string collection_id_param;
+ GURL path_url = GURL(chrome::kChromeSearchLocalNtpUrl).Resolve(path);
+ if (net::GetValueForKeyInQuery(path_url, "collection_id",
+ &collection_id_param)) {
+ ntp_background_image_info_requests_.emplace_back(base::TimeTicks::Now(),
+ callback);
+ ntp_background_service_->FetchCollectionImageInfo(collection_id_param);
+ } else {
+ callback.Run(nullptr);
+ }
+ return;
+ }
+
if (stripped_path == kOneGoogleBarScriptFilename) {
if (!one_google_bar_service_) {
callback.Run(nullptr);
@@ -695,7 +733,7 @@
void LocalNtpSource::OnCollectionInfoAvailable() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- if (ntp_background_requests_.empty())
+ if (ntp_background_collections_requests_.empty())
return;
scoped_refptr<base::RefCountedString> result;
@@ -707,7 +745,7 @@
result = base::RefCountedString::TakeString(&js);
base::TimeTicks now = base::TimeTicks::Now();
- for (const auto& request : ntp_background_requests_) {
+ for (const auto& request : ntp_background_collections_requests_) {
request.callback.Run(result);
base::TimeDelta delta = now - request.start_time;
UMA_HISTOGRAM_MEDIUM_TIMES(
@@ -723,7 +761,39 @@
delta);
}
}
- ntp_background_requests_.clear();
+ ntp_background_collections_requests_.clear();
+}
+
+void LocalNtpSource::OnCollectionImagesAvailable() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ if (ntp_background_image_info_requests_.empty())
+ return;
+
+ scoped_refptr<base::RefCountedString> result;
+ std::string js;
+ base::JSONWriter::Write(ConvertCollectionImageToDict(
+ ntp_background_service_->collection_images()),
+ &js);
+ js = "var coll_img = " + js + ";";
+ result = base::RefCountedString::TakeString(&js);
+
+ base::TimeTicks now = base::TimeTicks::Now();
+ for (const auto& request : ntp_background_image_info_requests_) {
+ request.callback.Run(result);
+ base::TimeDelta delta = now - request.start_time;
+ UMA_HISTOGRAM_MEDIUM_TIMES(
+ "NewTabPage.BackgroundService.Images.RequestLatency", delta);
+ // Any response where no images are returned is considered a failure.
+ if (ntp_background_service_->collection_images().empty()) {
+ UMA_HISTOGRAM_MEDIUM_TIMES(
+ "NewTabPage.BackgroundService.Images.RequestLatency.Failure", delta);
+ } else {
+ UMA_HISTOGRAM_MEDIUM_TIMES(
+ "NewTabPage.BackgroundService.Images.RequestLatency.Success", delta);
+ }
+ }
+ ntp_background_image_info_requests_.clear();
}
void LocalNtpSource::OnOneGoogleBarDataUpdated() {
diff --git a/chrome/browser/search/local_ntp_source.h b/chrome/browser/search/local_ntp_source.h
index 5fe6b67..1705666 100644
--- a/chrome/browser/search/local_ntp_source.h
+++ b/chrome/browser/search/local_ntp_source.h
@@ -89,6 +89,7 @@
// Overridden from NtpBackgroundServiceObserver:
void OnCollectionInfoAvailable() override;
+ void OnCollectionImagesAvailable() override;
// Overridden from OneGoogleBarServiceObserver:
void OnOneGoogleBarDataUpdated() override;
@@ -98,7 +99,8 @@
Profile* const profile_;
- std::vector<NtpBackgroundRequest> ntp_background_requests_;
+ std::vector<NtpBackgroundRequest> ntp_background_collections_requests_;
+ std::vector<NtpBackgroundRequest> ntp_background_image_info_requests_;
NtpBackgroundService* ntp_background_service_;