blob: 7b663688e0c191b2e5b23dc91a26f53a513f75b0 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/themes/ntp_background_service.h"
#include <string_view>
#include "base/barrier_closure.h"
#include "base/check_deref.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/observer_list.h"
#include "base/strings/strcat.h"
#include "base/time/time.h"
#include "components/application_locale_storage/application_locale_storage.h"
#include "components/search/ntp_features.h"
#include "components/themes/ntp_background.pb.h"
#include "components/version_info/version_info.h"
#include "net/base/load_flags.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
namespace {
// Command line param to override the collections base URL, e.g. for testing.
constexpr char kCollectionsBaseUrlCmdlineSwitch[] = "collections-base-url";
// The default base URL to download prod collections.
constexpr char kCollectionsBaseUrl[] = "https://clients3.google.com";
// The default base URL to download alpha collections.
constexpr char kAlphaCollectionsBaseUrl[] = "https://clients5.google.com";
// The MIME type of the POST data sent to the server.
constexpr char kProtoMimeType[] = "application/x-protobuf";
// The path relative to kCollectionsBaseUrl to download the proto of the
// complete list of wallpaper collections.
constexpr char kCollectionsPath[] =
"/cast/chromecast/home/wallpaper/collections?rt=b";
// The path relative to kCollectionsBaseUrl to download the metadata of the
// images in a collection.
constexpr char kCollectionImagesPath[] =
"/cast/chromecast/home/wallpaper/collection-images?rt=b";
// The path relative to kCollectionsBaseUrl to download the metadata of the
// 'next' image in a collection.
constexpr char kNextCollectionImagePath[] =
"/cast/chromecast/home/wallpaper/image?rt=b";
// Returns the configured collections base URL with |path| appended.
GURL GetUrl(std::string_view path) {
return GURL(base::CommandLine::ForCurrentProcess()->HasSwitch(
kCollectionsBaseUrlCmdlineSwitch)
? base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
kCollectionsBaseUrlCmdlineSwitch)
: base::FeatureList::IsEnabled(
ntp_features::kNtpAlphaBackgroundCollections)
? kAlphaCollectionsBaseUrl
: kCollectionsBaseUrl)
.Resolve(path);
}
} // namespace
NtpBackgroundService::NtpBackgroundService(
ApplicationLocaleStorage* application_locale_storage,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: application_locale_storage_(CHECK_DEREF(application_locale_storage)),
url_loader_factory_(url_loader_factory) {
default_image_options_ = GetImageOptions();
thumbnail_image_options_ = GetThumbnailImageOptions();
collections_api_url_ = GetUrl(kCollectionsPath);
collection_images_api_url_ = GetUrl(kCollectionImagesPath);
next_image_api_url_ = GetUrl(kNextCollectionImagePath);
}
NtpBackgroundService::~NtpBackgroundService() = default;
void NtpBackgroundService::Shutdown() {
for (auto& observer : observers_) {
observer.OnNtpBackgroundServiceShuttingDown();
}
DCHECK(observers_.empty());
}
void NtpBackgroundService::FetchCollectionInfo(
const std::string& filtering_label) {
// If a request is currently in progress, drop the new request.
if (collections_loader_ != nullptr) {
return;
}
collection_error_info_.ClearError();
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("backdrop_collection_names_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 customize (pencil) icon on the New Tab page."
data:
"The Backdrop protocol buffer messages. No user data is included."
destination: GOOGLE_OWNED_SERVICE
internal {
contacts {
email: "[email protected]"
}
}
user_data {
type: NONE
}
last_reviewed: "2023-06-13"
}
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
}
}
})");
ntp::background::GetCollectionsRequest request;
// The language field may include the country code (e.g. "en-US").
request.set_language(application_locale_storage_->Get());
request.add_filtering_label(filtering_label);
// Add some extra filtering information in case we need to target a specific
// milestone post release.
request.add_filtering_label(base::StrCat(
{filtering_label, ".M", version_info::GetMajorVersionNumber()}));
// Add filtering for Panorama feature.
request.add_filtering_label(base::StrCat({filtering_label, ".panorama"}));
request.add_filtering_label(base::StrCat({filtering_label, ".gm3"}));
if (base::FeatureList::IsEnabled(
ntp_features::kNtpBackgroundImageErrorDetection)) {
request.add_filtering_label(
base::StrCat({filtering_label, ".error_detection"}));
}
std::string serialized_proto;
request.SerializeToString(&serialized_proto);
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = collections_api_url_;
resource_request->method = "POST";
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
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)),
1024 * 1024);
}
void NtpBackgroundService::FetchCollectionInfo() {
// Calls `FetchCollectionInfo` method using the default filter.
FetchCollectionInfo(GetFilteringLabel());
}
void NtpBackgroundService::OnCollectionInfoFetchComplete(
std::unique_ptr<std::string> response_body) {
collection_info_.clear();
// The loader will be deleted when the request is handled.
std::unique_ptr<network::SimpleURLLoader> loader_deleter(
std::move(collections_loader_));
if (!response_body) {
// This represents network errors (i.e. the server did not provide a
// response).
DVLOG(1) << "Request failed with error: " << loader_deleter->NetError();
collection_error_info_.error_type = ErrorType::NET_ERROR;
collection_error_info_.net_error = loader_deleter->NetError();
NotifyObservers(FetchComplete::COLLECTION_INFO);
return;
}
ntp::background::GetCollectionsResponse collections_response;
if (!collections_response.ParseFromString(*response_body)) {
DVLOG(1) << "Deserializing Backdrop wallpaper proto for collection info "
"failed.";
collection_error_info_.error_type = ErrorType::SERVICE_ERROR;
NotifyObservers(FetchComplete::COLLECTION_INFO);
return;
}
for (int i = 0; i < collections_response.collections_size(); ++i) {
ntp::background::Collection collection =
collections_response.collections(i);
if (collection.preview_size() > 0 &&
collection.preview(0).has_image_url()) {
collection_info_.push_back(CollectionInfo::CreateFromProto(
collection, /*preview_image_url=*/AddOptionsToImageURL(
collection.preview(0).image_url(), thumbnail_image_options_)));
} else {
collection_info_.push_back(CollectionInfo::CreateFromProto(
collection, /*preview_image_url=*/std::nullopt));
}
}
NotifyObservers(FetchComplete::COLLECTION_INFO);
}
void NtpBackgroundService::FetchCollectionImageInfo(
const std::string& collection_id,
FetchCollectionImageCallback callback) {
FetchCollectionImageInfoInternal(
collection_id,
base::BindOnce(&NtpBackgroundService::OnCollectionImageInfoFetchComplete,
base::Unretained(this), collection_id,
std::move(callback)));
}
void NtpBackgroundService::FetchCollectionImageInfo(
const std::string& collection_id) {
collection_images_error_info_.ClearError();
// Ignore subsequent requests to fetch collection image info.
// TODO(crbug.com/40916951): Prioritize the latest request to fetch collection
// images.
if (!requested_collection_id_.empty()) {
return;
}
requested_collection_id_ = collection_id;
pending_image_url_header_loaders_.clear();
FetchCollectionImageInfoInternal(
collection_id,
base::BindOnce(
&NtpBackgroundService::OnCollectionImageInfoFetchComplete,
base::Unretained(this), collection_id,
base::BindOnce(&NtpBackgroundService::OnCollectionImageInfoReceived,
base::Unretained(this))));
}
void NtpBackgroundService::OnCollectionImageInfoFetchComplete(
const std::string& collection_id,
FetchCollectionImageCallback callback,
ntp::background::GetImagesInCollectionResponse images_response,
ErrorType error_type) {
std::vector<CollectionImage> collection_images;
if (error_type == ErrorType::NET_ERROR) {
// This represents network errors (i.e. the server did not provide
// a response).
DVLOG(1) << "Request failed with error: "
<< collection_images_error_info_.net_error;
} else if (error_type == ErrorType::SERVICE_ERROR) {
DVLOG(1) << "Deserializing Backdrop wallpaper proto for image "
"info failed.";
} else {
for (int i = 0; i < images_response.images_size(); ++i) {
const ntp::background::Image image = images_response.images(i);
collection_images.push_back(CollectionImage::CreateFromProto(
collection_id, image,
/*default_image_url=*/
AddOptionsToImageURL(image.image_url(), default_image_options_),
/*thumbnail_image_url=*/
AddOptionsToImageURL(image.image_url(), thumbnail_image_options_)));
}
}
// Execute callback with the fetched collection images data.
std::move(callback).Run(collection_images, error_type);
}
void NtpBackgroundService::OnCollectionImageInfoReceived(
const std::vector<CollectionImage>& collection_images,
ErrorType error_type) {
collection_images_.clear();
if (error_type != ErrorType::NONE) {
collection_images_error_info_.error_type = error_type;
NotifyObservers(FetchComplete::COLLECTION_IMAGE_INFO);
return;
}
collection_images_ = collection_images;
NotifyObservers(FetchComplete::COLLECTION_IMAGE_INFO);
}
void NtpBackgroundService::VerifyImageURL(
const GURL& url,
base::OnceCallback<void(int)> image_url_headers_received_callback) {
constexpr net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("ntp_image_url_verification",
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.). "
"This fetches a wallpaper image's link to make sure its "
"resource is reachable."
trigger:
"When the user has a non-uploaded background image set and "
"opens the New Tab Page, clicks the customize (pencil) icon "
"on the New Tab page, or clicks a theme "
"collection on the New Tab Page."
data:
"The HTTP headers for a NTP background image."
destination: GOOGLE_OWNED_SERVICE
internal {
contacts {
email: "[email protected]"
}
}
user_data {
type: NONE
}
last_reviewed: "2023-07-13"
}
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
}
}
})");
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->method = "GET";
resource_request->url = url;
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
std::unique_ptr<network::SimpleURLLoader> image_url_header_loader =
network::SimpleURLLoader::Create(std::move(resource_request),
traffic_annotation);
network::SimpleURLLoader* const image_url_header_loader_ptr =
image_url_header_loader.get();
image_url_header_loader_ptr->SetRetryOptions(
/*max_retries=*/3,
network::SimpleURLLoader::RetryMode::RETRY_ON_5XX |
network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE |
network::SimpleURLLoader::RETRY_ON_NAME_NOT_RESOLVED);
auto it = pending_image_url_header_loaders_.insert(
pending_image_url_header_loaders_.begin(),
std::move(image_url_header_loader));
image_url_header_loader_ptr->DownloadHeadersOnly(
url_loader_factory_.get(),
base::BindOnce(&NtpBackgroundService::OnImageURLHeadersFetchComplete,
base::Unretained(this), std::move(it),
std::move(image_url_headers_received_callback),
base::TimeTicks::Now()));
}
void NtpBackgroundService::OnImageURLHeadersFetchComplete(
URLLoaderList::iterator it,
base::OnceCallback<void(int)> image_url_headers_received_callback,
base::TimeTicks request_start,
scoped_refptr<net::HttpResponseHeaders> headers) {
if (pending_image_url_header_loaders_.empty()) {
return;
}
pending_image_url_header_loaders_.erase(it);
int headers_response_code =
headers ? headers->response_code() : net::ERR_FAILED;
UMA_HISTOGRAM_SPARSE("NewTabPage.BackgroundService.Images.Headers.StatusCode",
headers_response_code);
auto request_time = base::TimeTicks::Now() - request_start;
UMA_HISTOGRAM_TIMES(
"NewTabPage.BackgroundService.Images.Headers.RequestLatency",
request_time);
if (headers_response_code == net::HTTP_OK) {
UMA_HISTOGRAM_TIMES(
"NewTabPage.BackgroundService.Images.Headers.RequestLatency.Ok",
request_time);
} else if (headers_response_code == net::HTTP_NOT_FOUND) {
UMA_HISTOGRAM_TIMES(
"NewTabPage.BackgroundService.Images.Headers.RequestLatency.NotFound",
request_time);
} else {
UMA_HISTOGRAM_TIMES(
"NewTabPage.BackgroundService.Images.Headers.RequestLatency.Other",
request_time);
}
std::move(image_url_headers_received_callback).Run(headers_response_code);
}
void NtpBackgroundService::FetchReplacementCollectionPreviewImage(
const std::string& collection_id,
FetchReplacementImageCallback fetch_replacement_image_callback) {
FetchCollectionImageInfoInternal(
collection_id,
base::BindOnce(&NtpBackgroundService::
OnFetchReplacementCollectionPreviewImageComplete,
base::Unretained(this),
std::move(fetch_replacement_image_callback)));
}
void NtpBackgroundService::OnFetchReplacementCollectionPreviewImageComplete(
FetchReplacementImageCallback fetch_replacement_image_callback,
ntp::background::GetImagesInCollectionResponse images_response,
ErrorType error_type) {
if (error_type != ErrorType::NONE || images_response.images_size() == 0) {
std::move(fetch_replacement_image_callback).Run(std::nullopt);
return;
}
// Attempt to find an image URL in the collection that works. We start with
// the first image's index, and increment, if needed, in the callback
// |OnReplacementCollectionPreviewImageHeadersReceived|.
const int replacement_image_index = 0;
const GURL replacement_image_url = AddOptionsToImageURL(
images_response.images(replacement_image_index).image_url(),
thumbnail_image_options_);
VerifyImageURL(
replacement_image_url,
base::BindOnce(
&NtpBackgroundService::
OnReplacementCollectionPreviewImageHeadersReceived,
base::Unretained(this), std::move(fetch_replacement_image_callback),
images_response, replacement_image_index, replacement_image_url));
}
void NtpBackgroundService::OnReplacementCollectionPreviewImageHeadersReceived(
FetchReplacementImageCallback fetch_replacement_image_callback,
ntp::background::GetImagesInCollectionResponse images_response,
int replacement_image_index,
const GURL& replacement_image_url,
int headers_response_code) {
if (headers_response_code == net::HTTP_OK) {
std::move(fetch_replacement_image_callback).Run(replacement_image_url);
} else if (replacement_image_index == images_response.images_size() - 1) {
std::move(fetch_replacement_image_callback).Run(std::nullopt);
} else {
replacement_image_index++;
const GURL next_replacement_image_url = AddOptionsToImageURL(
images_response.images(replacement_image_index).image_url(),
thumbnail_image_options_);
VerifyImageURL(
next_replacement_image_url,
base::BindOnce(&NtpBackgroundService::
OnReplacementCollectionPreviewImageHeadersReceived,
base::Unretained(this),
std::move(fetch_replacement_image_callback),
images_response, replacement_image_index,
next_replacement_image_url));
}
}
void NtpBackgroundService::FetchNextCollectionImage(
const std::string& collection_id,
const std::optional<std::string>& resume_token) {
next_image_error_info_.ClearError();
if (next_image_loader_ != nullptr) {
return;
}
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("backdrop_next_image_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 customize (pencil) icon on the New Tab page."
data:
"The Backdrop protocol buffer messages. ApplicationLocale is "
"included in the request."
destination: GOOGLE_OWNED_SERVICE
internal {
contacts {
email: "[email protected]"
}
}
user_data {
type: NONE
}
last_reviewed: "2023-06-13"
}
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_next_image_collection_id_ = collection_id;
requested_next_image_resume_token_ = resume_token.value_or("");
ntp::background::GetImageFromCollectionRequest request;
*request.add_collection_ids() = requested_next_image_collection_id_;
request.set_resume_token(requested_next_image_resume_token_);
// The language field may include the country code (e.g. "en-US").
request.set_language(application_locale_storage_->Get());
std::string serialized_proto;
request.SerializeToString(&serialized_proto);
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = next_image_api_url_;
resource_request->method = "POST";
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
next_image_loader_ = network::SimpleURLLoader::Create(
std::move(resource_request), traffic_annotation);
next_image_loader_->AttachStringForUpload(serialized_proto, kProtoMimeType);
next_image_loader_->DownloadToString(
url_loader_factory_.get(),
base::BindOnce(&NtpBackgroundService::OnNextImageInfoFetchComplete,
base::Unretained(this)),
1024 * 1024);
}
void NtpBackgroundService::OnNextImageInfoFetchComplete(
std::unique_ptr<std::string> response_body) {
// The loader will be deleted when the request is handled.
std::unique_ptr<network::SimpleURLLoader> loader_deleter(
std::move(next_image_loader_));
if (!response_body) {
// This represents network errors (i.e. the server did not provide a
// response).
DVLOG(1) << "Request failed with error: " << loader_deleter->NetError();
next_image_error_info_.error_type = ErrorType::NET_ERROR;
next_image_error_info_.net_error = loader_deleter->NetError();
NotifyObservers(FetchComplete::NEXT_IMAGE_INFO);
return;
}
ntp::background::GetImageFromCollectionResponse image_response;
if (!image_response.ParseFromString(*response_body)) {
DVLOG(1) << "Deserializing Backdrop wallpaper proto for next image failed.";
next_image_error_info_.error_type = ErrorType::SERVICE_ERROR;
NotifyObservers(FetchComplete::NEXT_IMAGE_INFO);
return;
}
const ntp::background::Image image = image_response.image();
next_image_ = CollectionImage::CreateFromProto(
requested_next_image_collection_id_, image,
/*default_image_url=*/
AddOptionsToImageURL(image.image_url(), default_image_options_),
/*thumbnail_image_url=*/
AddOptionsToImageURL(image.image_url(), thumbnail_image_options_));
next_image_resume_token_ = image_response.resume_token();
NotifyObservers(FetchComplete::NEXT_IMAGE_INFO);
}
void NtpBackgroundService::FetchCollectionImageInfoInternal(
const std::string& collection_id,
base::OnceCallback<void(ntp::background::GetImagesInCollectionResponse,
ErrorType)> collection_images_received_callback) {
collection_images_error_info_.ClearError();
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 customize (pencil) icon on the New Tab page."
data:
"The Backdrop protocol buffer messages. No user data is included."
destination: GOOGLE_OWNED_SERVICE
internal {
contacts {
email: "[email protected]"
}
}
user_data {
type: NONE
}
last_reviewed: "2023-06-13"
}
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
}
}
})");
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = collection_images_api_url_;
resource_request->method = "POST";
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
std::unique_ptr<network::SimpleURLLoader> collection_image_info_loader =
network::SimpleURLLoader::Create(std::move(resource_request),
traffic_annotation);
network::SimpleURLLoader* const collection_image_info_loader_ptr =
collection_image_info_loader.get();
collection_image_info_loader->SetRetryOptions(
/*max_retries=*/3,
network::SimpleURLLoader::RetryMode::RETRY_ON_5XX |
network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE |
network::SimpleURLLoader::RETRY_ON_NAME_NOT_RESOLVED);
auto it = pending_collection_image_info_loaders_.insert(
pending_collection_image_info_loaders_.begin(),
std::move(collection_image_info_loader));
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(application_locale_storage_->Get());
std::string serialized_proto;
request.SerializeToString(&serialized_proto);
collection_image_info_loader_ptr->AttachStringForUpload(serialized_proto,
kProtoMimeType);
collection_image_info_loader_ptr->DownloadToString(
url_loader_factory_.get(),
base::BindOnce(
[](URLLoaderList& pending_collection_image_info_loaders,
URLLoaderList::iterator it,
base::OnceCallback<void(
ntp::background::GetImagesInCollectionResponse, ErrorType)>
collection_images_received_callback,
std::unique_ptr<std::string> response_body) {
if (pending_collection_image_info_loaders.empty()) {
return;
}
pending_collection_image_info_loaders.erase(it);
ntp::background::GetImagesInCollectionResponse images_response;
if (!response_body) {
std::move(collection_images_received_callback)
.Run(std::move(images_response), ErrorType::NET_ERROR);
} else if (!images_response.ParseFromString(*response_body)) {
std::move(collection_images_received_callback)
.Run(std::move(images_response), ErrorType::SERVICE_ERROR);
} else {
std::move(collection_images_received_callback)
.Run(std::move(images_response), ErrorType::NONE);
}
},
std::ref(pending_collection_image_info_loaders_), std::move(it),
std::move(collection_images_received_callback)),
1024 * 1024);
}
const std::vector<CollectionInfo>& NtpBackgroundService::collection_info()
const {
return collection_info_;
}
const std::vector<CollectionImage>& NtpBackgroundService::collection_images()
const {
return collection_images_;
}
void NtpBackgroundService::AddObserver(NtpBackgroundServiceObserver* observer) {
observers_.AddObserver(observer);
}
void NtpBackgroundService::RemoveObserver(
NtpBackgroundServiceObserver* observer) {
observers_.RemoveObserver(observer);
}
bool NtpBackgroundService::IsValidBackdropUrl(const GURL& url) const {
for (auto& image : collection_images_) {
if (image.image_url == url) {
return true;
}
}
return false;
}
bool NtpBackgroundService::IsValidBackdropCollection(
const std::string& collection_id) const {
for (auto& collection : collection_info_) {
if (collection.collection_id == collection_id) {
return true;
}
}
return false;
}
void NtpBackgroundService::AddValidBackdropUrlForTesting(const GURL& url) {
CollectionImage image;
image.image_url = url;
collection_images_.push_back(image);
}
void NtpBackgroundService::AddValidBackdropCollectionForTesting(
const std::string& collection_id) {
CollectionInfo collection;
collection.collection_id = collection_id;
collection_info_.push_back(collection);
}
void NtpBackgroundService::AddValidBackdropUrlWithThumbnailForTesting(
const GURL& url,
const GURL& thumbnail_url) {
CollectionImage image;
image.image_url = url;
image.thumbnail_image_url = thumbnail_url;
collection_images_.push_back(image);
}
void NtpBackgroundService::SetNextCollectionImageForTesting(
const CollectionImage& image) {
next_image_ = image;
}
const GURL& NtpBackgroundService::GetThumbnailUrl(const GURL& image_url) {
for (auto& image : collection_images_) {
if (image.image_url == image_url) {
return image.thumbnail_image_url;
}
}
return GURL::EmptyGURL();
}
void NtpBackgroundService::NotifyObservers(FetchComplete fetch_complete) {
for (auto& observer : observers_) {
switch (fetch_complete) {
case FetchComplete::COLLECTION_INFO:
observer.OnCollectionInfoAvailable();
break;
case FetchComplete::COLLECTION_IMAGE_INFO:
requested_collection_id_.clear();
observer.OnCollectionImagesAvailable();
break;
case FetchComplete::NEXT_IMAGE_INFO:
observer.OnNextCollectionImageAvailable();
break;
}
}
}
GURL NtpBackgroundService::GetCollectionsLoadURLForTesting() const {
return collections_api_url_;
}
GURL NtpBackgroundService::GetImagesURLForTesting() const {
return collection_images_api_url_;
}
GURL NtpBackgroundService::GetNextImageURLForTesting() const {
return next_image_api_url_;
}