blob: 445d3adcdb7807722269e85a9ce63b4e25dc90ee [file] [log] [blame]
// Copyright 2018 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 "chrome/browser/chromeos/backdrop_wallpaper_handlers/backdrop_wallpaper_handlers.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/strings/string_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/backdrop_wallpaper_handlers/backdrop_wallpaper.pb.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/common/extensions/api/wallpaper_private.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/constants/chromeos_switches.h"
#include "chromeos/constants/devicetype.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/load_flags.h"
#include "url/gurl.h"
namespace {
// The MIME type of the POST data sent to the server.
constexpr char kProtoMimeType[] = "application/x-protobuf";
// The url to download the proto of the complete list of wallpaper collections.
constexpr char kBackdropCollectionsUrl[] =
"https://clients3.google.com/cast/chromecast/home/wallpaper/"
"collections?rt=b";
// The url to download the proto of a specific wallpaper collection.
constexpr char kBackdropImagesUrl[] =
"https://clients3.google.com/cast/chromecast/home/wallpaper/"
"collection-images?rt=b";
// The url to download the proto of the info of a surprise me wallpaper.
constexpr char kBackdropSurpriseMeImageUrl[] =
"https://clients3.google.com/cast/chromecast/home/wallpaper/"
"image?rt=b";
// The label used to return exclusive content or filter unwanted images.
constexpr char kFilteringLabel[] = "chromebook";
// The label used to return exclusive content for Google branded chromebooks.
constexpr char kGoogleDeviceFilteringLabel[] = "google_branded_chromebook";
// Returns the corresponding test url if |kTestWallpaperServer| is present,
// otherwise returns |url| as is. See https://crbug.com/914144.
std::string MaybeConvertToTestUrl(std::string url) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
chromeos::switches::kTestWallpaperServer)) {
base::ReplaceFirstSubstringAfterOffset(&url, 0, "clients3",
"chromecast-dev.sandbox");
} else if (base::FeatureList::IsEnabled(
chromeos::features::kUseWallpaperStagingUrl)) {
base::ReplaceFirstSubstringAfterOffset(&url, 0, "clients3",
"chromecast-staging.sandbox");
}
return url;
}
} // namespace
namespace backdrop_wallpaper_handlers {
// Helper class for handling Backdrop service POST requests.
class BackdropFetcher {
public:
using OnFetchComplete = base::OnceCallback<void(const std::string& response)>;
BackdropFetcher() = default;
~BackdropFetcher() = default;
// Starts downloading the proto. |request_body| is a serialized proto and
// will be used as the upload body.
void Start(const GURL& url,
const std::string& request_body,
const net::NetworkTrafficAnnotationTag& traffic_annotation,
OnFetchComplete callback) {
DCHECK(!simple_loader_ && callback_.is_null());
callback_ = std::move(callback);
SystemNetworkContextManager* system_network_context_manager =
g_browser_process->system_network_context_manager();
// In unit tests, the browser process can return a null context manager.
if (!system_network_context_manager) {
std::move(callback_).Run(std::string());
return;
}
network::mojom::URLLoaderFactory* loader_factory =
system_network_context_manager->GetURLLoaderFactory();
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = url;
resource_request->method = "POST";
resource_request->load_flags =
net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
simple_loader_ = network::SimpleURLLoader::Create(
std::move(resource_request), traffic_annotation);
simple_loader_->AttachStringForUpload(request_body, kProtoMimeType);
// |base::Unretained| is safe because this instance outlives
// |simple_loader_|.
simple_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory, base::BindOnce(&BackdropFetcher::OnURLFetchComplete,
base::Unretained(this)));
}
private:
// Called when the download completes.
void OnURLFetchComplete(std::unique_ptr<std::string> response_body) {
if (!response_body) {
int response_code = -1;
if (simple_loader_->ResponseInfo() &&
simple_loader_->ResponseInfo()->headers) {
response_code =
simple_loader_->ResponseInfo()->headers->response_code();
}
LOG(ERROR) << "Downloading Backdrop wallpaper proto failed with error "
"code: "
<< response_code;
simple_loader_.reset();
std::move(callback_).Run(std::string());
return;
}
simple_loader_.reset();
std::move(callback_).Run(*response_body);
}
// The url loader for the Backdrop service request.
std::unique_ptr<network::SimpleURLLoader> simple_loader_;
// The fetcher request callback.
OnFetchComplete callback_;
DISALLOW_COPY_AND_ASSIGN(BackdropFetcher);
};
CollectionInfoFetcher::CollectionInfoFetcher() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
CollectionInfoFetcher::~CollectionInfoFetcher() = default;
void CollectionInfoFetcher::Start(OnCollectionsInfoFetched callback) {
DCHECK(!backdrop_fetcher_ && callback_.is_null());
callback_ = std::move(callback);
backdrop_fetcher_ = std::make_unique<BackdropFetcher>();
backdrop::GetCollectionsRequest request;
// The language field may include the country code (e.g. "en-US").
request.set_language(g_browser_process->GetApplicationLocale());
request.add_filtering_label(kFilteringLabel);
if (chromeos::IsGoogleBrandedDevice())
request.add_filtering_label(kGoogleDeviceFilteringLabel);
std::string serialized_proto;
request.SerializeToString(&serialized_proto);
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("backdrop_collection_names_download",
R"(
semantics {
sender: "ChromeOS Wallpaper Picker"
description:
"The ChromeOS Wallpaper Picker extension 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 downloaded from the Backdrop wallpaper service."
trigger:
"When ChromeOS Wallpaper Picker extension is open, and "
"BUILDFLAG(GOOGLE_CHROME_BRANDING) is defined."
data:
"The Backdrop protocol buffer messages. No user data is included."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: NO
setting:
"NA"
policy_exception_justification:
"Not implemented, considered not necessary."
})");
// |base::Unretained| is safe because this instance outlives
// |backdrop_fetcher_|.
backdrop_fetcher_->Start(
GURL(MaybeConvertToTestUrl(kBackdropCollectionsUrl)), serialized_proto,
traffic_annotation,
base::BindOnce(&CollectionInfoFetcher::OnResponseFetched,
base::Unretained(this)));
}
void CollectionInfoFetcher::OnResponseFetched(const std::string& response) {
std::vector<backdrop::Collection> collections;
backdrop::GetCollectionsResponse collections_response;
bool success = false;
if (response.empty() || !collections_response.ParseFromString(response)) {
LOG(ERROR) << "Deserializing Backdrop wallpaper proto for collection info "
"failed.";
} else {
success = true;
for (const auto& collection : collections_response.collections())
collections.push_back(collection);
}
backdrop_fetcher_.reset();
std::move(callback_).Run(success, collections);
}
ImageInfoFetcher::ImageInfoFetcher(const std::string& collection_id)
: collection_id_(collection_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
ImageInfoFetcher::~ImageInfoFetcher() = default;
void ImageInfoFetcher::Start(OnImagesInfoFetched callback) {
DCHECK(!backdrop_fetcher_ && callback_.is_null());
callback_ = std::move(callback);
backdrop_fetcher_ = std::make_unique<BackdropFetcher>();
backdrop::GetImagesInCollectionRequest request;
// The language field may include the country code (e.g. "en-US").
request.set_language(g_browser_process->GetApplicationLocale());
request.set_collection_id(collection_id_);
request.add_filtering_label(kFilteringLabel);
if (chromeos::IsGoogleBrandedDevice())
request.add_filtering_label(kGoogleDeviceFilteringLabel);
std::string serialized_proto;
request.SerializeToString(&serialized_proto);
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("backdrop_images_info_download", R"(
semantics {
sender: "ChromeOS Wallpaper Picker"
description:
"When user clicks on a particular wallpaper collection on the "
"ChromeOS Wallpaper Picker, it displays the preview of the iamges "
"and descriptive texts for each image. Such information is "
"downloaded from the Backdrop wallpaper service."
trigger:
"When ChromeOS Wallpaper Picker extension is open, "
"BUILDFLAG(GOOGLE_CHROME_BRANDING) is defined and user clicks on a "
"particular collection."
data:
"The Backdrop protocol buffer messages. No user data is included."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: NO
setting:
"NA"
policy_exception_justification:
"Not implemented, considered not necessary."
})");
// |base::Unretained| is safe because this instance outlives
// |backdrop_fetcher_|.
backdrop_fetcher_->Start(GURL(MaybeConvertToTestUrl(kBackdropImagesUrl)),
serialized_proto, traffic_annotation,
base::BindOnce(&ImageInfoFetcher::OnResponseFetched,
base::Unretained(this)));
}
void ImageInfoFetcher::OnResponseFetched(const std::string& response) {
std::vector<backdrop::Image> images;
backdrop::GetImagesInCollectionResponse images_response;
bool success = false;
if (response.empty() || !images_response.ParseFromString(response)) {
LOG(ERROR) << "Deserializing Backdrop wallpaper proto for collection "
<< collection_id_ << " failed";
} else {
success = true;
for (const auto& image : images_response.images())
images.push_back(image);
}
backdrop_fetcher_.reset();
std::move(callback_).Run(success, images);
}
SurpriseMeImageFetcher::SurpriseMeImageFetcher(const std::string& collection_id,
const std::string& resume_token)
: collection_id_(collection_id), resume_token_(resume_token) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
SurpriseMeImageFetcher::~SurpriseMeImageFetcher() = default;
void SurpriseMeImageFetcher::Start(OnSurpriseMeImageFetched callback) {
DCHECK(!backdrop_fetcher_ && callback_.is_null());
callback_ = std::move(callback);
backdrop_fetcher_ = std::make_unique<BackdropFetcher>();
backdrop::GetImageFromCollectionRequest request;
// The language field may include the country code (e.g. "en-US").
request.set_language(g_browser_process->GetApplicationLocale());
request.add_collection_ids(collection_id_);
request.add_filtering_label(kFilteringLabel);
if (chromeos::IsGoogleBrandedDevice())
request.add_filtering_label(kGoogleDeviceFilteringLabel);
if (!resume_token_.empty())
request.set_resume_token(resume_token_);
std::string serialized_proto;
request.SerializeToString(&serialized_proto);
const net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("backdrop_surprise_me_image_download",
R"(
semantics {
sender: "ChromeOS Wallpaper Picker"
description:
"POST request that fetches information about the wallpaper that "
"should be set next for the user that enabled surprise me feature "
"in the Chrome OS Wallpaper Picker. For these users, wallpaper is "
"periodically changed to a random wallpaper selected by the "
"Backdrop wallpaper service."
trigger:
"When ChromeOS Wallpaper Picker extension is open, "
"BUILDFLAG(GOOGLE_CHROME_BRANDING) is defined and user turns on "
"the surprise me feature."
data:
"The Backdrop protocol buffer messages. No user data is included."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: NO
setting:
"NA"
policy_exception_justification:
"Not implemented, considered not necessary."
})");
// |base::Unretained| is safe because this instance outlives
// |backdrop_fetcher_|.
backdrop_fetcher_->Start(
GURL(MaybeConvertToTestUrl(kBackdropSurpriseMeImageUrl)),
serialized_proto, traffic_annotation,
base::BindOnce(&SurpriseMeImageFetcher::OnResponseFetched,
base::Unretained(this)));
}
void SurpriseMeImageFetcher::OnResponseFetched(const std::string& response) {
backdrop::GetImageFromCollectionResponse surprise_me_image_response;
if (response.empty() ||
!surprise_me_image_response.ParseFromString(response)) {
LOG(ERROR) << "Deserializing surprise me wallpaper proto for collection "
<< collection_id_ << " failed";
backdrop_fetcher_.reset();
std::move(callback_).Run(/*success=*/false, backdrop::Image(),
std::string());
return;
}
backdrop_fetcher_.reset();
std::move(callback_).Run(/*success=*/true, surprise_me_image_response.image(),
surprise_me_image_response.resume_token());
}
} // namespace backdrop_wallpaper_handlers